mcan_core/lib.rs
1#![no_std]
2#![warn(missing_docs)]
3
4//! `mcan-core` provides a set of essential abstractions that serves as a thin
5//! integration layer between the platform independent [`mcan`] crate and
6//! platform specific HAL crates (in documentation also referred to as _target
7//! HALs_).
8//!
9//! Traits from this crate are not supposed to be implemented by the
10//! application developer; implementations should be provided by target HALs.
11//!
12//! Integrators of this crate into any given target HAL are responsible for
13//! soundness of trait implementations and conforming to their respective safety
14//! prerequisites.
15//!
16//! [`mcan`]: <https://docs.rs/crate/mcan/>
17
18pub use fugit;
19
20/// Trait representing CAN peripheral identity
21///
22/// Types implementing this trait are expected to be used as a marker types that
23/// serve the purpose of identifying specific instances of CAN peripherals
24/// available on the platform (as there might be more than one). It only conveys
25/// *where* the CAN peripheral HW register is located, not necessarily that it
26/// can be accessed. The latter is expressed by the [`Dependencies`] trait.
27///
28/// It is also useful for associating [`Dependencies`] with specific [`CanId`]
29/// and setting up additional type constraints preventing application developers
30/// from constructing CAN abstractions with incompatible sets of dependencies.
31///
32/// More details in [`Dependencies`] documentation.
33///
34/// # Safety
35/// `CanId::ADDRESS` points to the start of a valid HW register of a CAN
36/// peripheral
37///
38/// # Examples
39/// ```no_run
40/// use mcan_core::CanId;
41///
42/// pub enum Can0 {}
43///
44/// unsafe impl CanId for Can0 {
45/// const ADDRESS: *const () = 0xDEAD0000 as *const _;
46/// }
47///
48/// pub enum Can1 {}
49///
50/// unsafe impl CanId for Can1 {
51/// const ADDRESS: *const () = 0xBEEF0000 as *const _;
52/// }
53/// ```
54pub unsafe trait CanId {
55 /// Static address of HW register controlling corresponding CAN peripheral
56 const ADDRESS: *const ();
57}
58
59/// Trait representing CAN peripheral dependencies
60///
61/// Structs implementing [`Dependencies`] should
62/// - enclose all object representable dependencies of [`CanId`] and release
63/// them upon destruction
64/// - be constructible only when it is safe and sound to interact with the CAN
65/// peripheral (respective clocks and pins have been already configured)
66/// - be a singleton (only a single instance of [`Dependencies`] for a specific
67/// [`CanId`] must exist at the same time)
68///
69/// in order to prevent aliasing and guarantee that high level abstractions
70/// provided by [`mcan`] are sole owners of the peripheral.
71///
72/// Depending on target HAL API capabilities this can be assured either at
73/// compile-time by type constraints and/or by fallible [`Dependencies`] struct
74/// construction.
75///
76/// # Safety
77/// While the [`Dependencies`] type instance exists
78/// - Start address of the eligible memory region for `Message RAM` allocation
79/// must not change
80/// - CAN related clocks must not change
81/// - CAN related pins modes must not change
82/// - The HW register must be neither safely accessible by the application
83/// developer nor accessed in other parts of the target HAL
84///
85/// # Example
86/// ```no_run
87/// // This is just an example implementation, it is up to the `mcan`
88/// // integrator how to guarantee the soundness of `mcan` usage
89/// // with the target HAL API.
90/// # mod pac {
91/// # pub struct CAN0;
92/// # impl CAN0 {
93/// # const PTR: *const u8 = 0xDEAD0000 as *const _;
94/// # }
95/// # pub struct CAN1;
96/// # impl CAN1 {
97/// # const PTR: *const u8 = 0xBEEF0000 as *const _;
98/// # }
99/// # }
100/// # mod hal {
101/// # pub mod identities {
102/// # pub enum Can0 {}
103/// # pub enum Can1 {}
104/// # }
105/// # }
106/// # trait PeripheralClockId {}
107/// # struct PeripheralClock<ID: PeripheralClockId> {
108/// # __: core::marker::PhantomData<ID>
109/// # }
110/// # impl<ID: PeripheralClockId> PeripheralClock<ID> {
111/// # fn frequency(&self) -> HertzU32 {
112/// # HertzU32::from_raw(123)
113/// # }
114/// # }
115/// # struct HostClockToken;
116/// # impl HostClockToken {
117/// # fn frequency(&self) -> HertzU32 {
118/// # HertzU32::from_raw(123)
119/// # }
120/// # }
121/// # struct HostClock;
122/// # impl HostClock {
123/// # fn register_new_user(&mut self) -> HostClockToken { HostClockToken }
124/// # fn unregister_user(&mut self, _: HostClockToken) -> Result<(), ()> { Ok(()) }
125/// # }
126/// # struct Pin<ID, MODE> {
127/// # __: core::marker::PhantomData<(ID, MODE)>
128/// # }
129/// # struct PA22;
130/// # struct PA23;
131/// # struct PB12;
132/// # struct PB13;
133/// # struct AlternateI;
134/// # struct AlternateH;
135/// use fugit::HertzU32;
136/// use mcan_core::CanId;
137///
138/// // In this example, `CanId` types are proper zero-sized marker
139/// // types and one can observe that information about HW register
140/// // addressing is somewhat duplicated between `pac::CAN{0, 1}`
141/// // and `Can{0, 1}`.
142/// //
143/// // The HAL design in this example assumes that a marker/identity type
144/// // is reused in related contexts allowing for elaborate type constraints
145/// // between abstractions from different modules (like peripheral clock
146/// // for CAN and its HW register).
147/// //
148/// // In a simpler setup, `CanId` could be just implemented by the low
149/// // level CAN type from PAC.
150/// unsafe impl CanId for hal::identities::Can0 {
151/// const ADDRESS: *const () = 0xDEAD0000 as *const _;
152/// }
153///
154/// unsafe impl CanId for hal::identities::Can1 {
155/// const ADDRESS: *const () = 0xBEEF0000 as *const _;
156/// }
157///
158/// pub struct Dependencies<ID: PeripheralClockId, RX, TX, CAN> {
159/// // This example design assumes runtime tracking of host clock
160/// // users à la reference counting. `HostClock` should not be
161/// // reconfigurable while having `> 0` users.
162/// host_clock_token: HostClockToken,
163/// // Clock object representing the CAN specific asynchronous clock
164/// can_peripheral_clock: PeripheralClock<ID>,
165/// // Opaque field reserved for RX pin
166/// rx: RX,
167/// // Opaque field reserved for TX pin
168/// tx: TX,
169/// // Opaque field reserved for the CAN HW register type (from PAC)
170/// can: CAN,
171/// }
172///
173/// impl<ID: PeripheralClockId, RX, TX, CAN> Dependencies<ID, RX, TX, CAN> {
174/// // Constructor that additionally registers a new user of the host clock
175/// pub fn new<S>(
176/// host_clock: &mut HostClock,
177/// can_peripheral_clock: PeripheralClock<ID>,
178/// rx: RX,
179/// tx: TX,
180/// can: CAN,
181/// ) -> Self
182/// {
183/// Self {
184/// host_clock_token: host_clock.register_new_user(),
185/// can_peripheral_clock,
186/// rx,
187/// tx,
188/// can,
189/// }
190/// }
191/// // Destructor that additionally unregisters from the host clock
192/// pub fn free(self, host_clock: &mut HostClock) -> (PeripheralClock<ID>, RX, TX, CAN)
193/// {
194/// let Self {
195/// host_clock_token,
196/// can_peripheral_clock,
197/// rx,
198/// tx,
199/// can,
200/// ..
201/// } = self;
202/// host_clock.unregister_user(host_clock_token).expect("Host clock has invalid amount of users!");
203/// (can_peripheral_clock, rx, tx, can)
204/// }
205/// }
206///
207/// // Trait is only implemented for valid combinations of dependencies.
208/// unsafe impl<ID, RX, TX, CAN> mcan_core::Dependencies<ID> for Dependencies<ID, RX, TX, CAN>
209/// where
210/// ID: CanId + PeripheralClockId,
211/// RX: RxPin<ValidFor = ID>,
212/// TX: TxPin<ValidFor = ID>,
213/// CAN: OwnedPeripheral<Represents = ID>,
214/// {
215/// fn eligible_message_ram_start(&self) -> *const () {
216/// 0x2000_0000 as _
217/// }
218///
219/// fn host_clock(&self) -> HertzU32 {
220/// self.host_clock_token.frequency()
221/// }
222///
223/// fn can_clock(&self) -> HertzU32 {
224/// self.can_peripheral_clock.frequency()
225/// }
226/// }
227///
228/// // Trait introduced in order to get 1:1 mapping from the identity type to the PAC type.
229/// //
230/// // Again, in a simpler setup, `CanId` could just be implemented by the low
231/// // level CAN type from PAC.
232/// trait OwnedPeripheral {
233/// type Represents: CanId;
234/// }
235///
236/// impl OwnedPeripheral for pac::CAN0 {
237/// type Represents = hal::identities::Can0;
238/// }
239///
240/// impl OwnedPeripheral for pac::CAN1 {
241/// type Represents = hal::identities::Can1;
242/// }
243///
244/// trait RxPin {
245/// type ValidFor: CanId;
246/// }
247///
248/// trait TxPin {
249/// type ValidFor: CanId;
250/// }
251///
252/// impl RxPin for Pin<PA23, AlternateI> {
253/// type ValidFor = hal::identities::Can0;
254/// }
255///
256/// impl TxPin for Pin<PA22, AlternateI> {
257/// type ValidFor = hal::identities::Can0;
258/// }
259///
260/// impl RxPin for Pin<PB13, AlternateH> {
261/// type ValidFor = hal::identities::Can1;
262/// }
263///
264/// impl TxPin for Pin<PB12, AlternateH> {
265/// type ValidFor = hal::identities::Can1;
266/// }
267/// ```
268///
269/// [`mcan`]: <https://docs.rs/crate/mcan/>
270pub unsafe trait Dependencies<Id: CanId> {
271 /// Pointer to the start of memory that can be used for `Message RAM`.
272 ///
273 /// The two least significant bytes must be zero, or in other words, the
274 /// address must be aligned to `u16::MAX + 1`.
275 ///
276 /// MCAN uses 16-bit addressing internally. In order to validate the
277 /// correctness of the `Message RAM` placement, the target HAL has to
278 /// provide information about its absolute position in RAM.
279 fn eligible_message_ram_start(&self) -> *const ();
280 /// Frequency of the host / main / CPU clock.
281 ///
282 /// MCAN uses the host clock for most of its internal operations and its
283 /// speed has to be equal to or faster than the CAN specific asynchronous
284 /// clock.
285 fn host_clock(&self) -> fugit::HertzU32;
286 /// Frequency of the CAN specific asynchronous clock.
287 ///
288 /// MCAN uses a separate asynchronous clock for signaling / sampling and
289 /// as such it should have reasonably high precision. Its speed has to
290 /// be equal to or slower than the host clock.
291 fn can_clock(&self) -> fugit::HertzU32;
292}