kernel/platform/
chip.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//! Interfaces for implementing microcontrollers in Tock.
6
7use crate::platform::mpu;
8use crate::syscall;
9use core::fmt::Write;
10
11/// Interface for individual MCUs.
12///
13/// The trait defines chip-specific properties of Tock's operation. These
14/// include whether and which memory protection mechanism and scheduler_timer to
15/// use, how to switch between the kernel and userland applications, and how to
16/// handle hardware events.
17///
18/// Each microcontroller should define a struct and implement this trait.
19pub trait Chip {
20    /// The particular Memory Protection Unit (MPU) for this chip.
21    type MPU: mpu::MPU;
22
23    /// Provider to query the currently running thread ID.
24    type ThreadIdProvider: ThreadIdProvider;
25
26    /// The implementation of the interface between userspace and the kernel for
27    /// this specific chip. Likely this is architecture specific, but individual
28    /// chips may have various custom requirements.
29    type UserspaceKernelBoundary: syscall::UserspaceKernelBoundary;
30
31    /// The kernel calls this function to tell the chip to check for all pending
32    /// interrupts and to correctly dispatch them to the peripheral drivers for
33    /// the chip.
34    ///
35    /// This function should loop internally until all interrupts have been
36    /// handled. It is ok, however, if an interrupt occurs after the last check
37    /// but before this function returns. The kernel will handle this edge case.
38    fn service_pending_interrupts(&self);
39
40    /// Ask the chip to check if there are any pending interrupts.
41    fn has_pending_interrupts(&self) -> bool;
42
43    /// Returns a reference to the implementation for the MPU on this chip.
44    fn mpu(&self) -> &Self::MPU;
45
46    /// Returns a reference to the implementation for the interface between
47    /// userspace and kernelspace.
48    fn userspace_kernel_boundary(&self) -> &Self::UserspaceKernelBoundary;
49
50    /// Called when there is nothing left for the chip to do and it should enter
51    /// a low power sleep state. This low power sleep state should allow
52    /// interrupts to still be active so that the next interrupt event wakes the
53    /// chip and resumes the scheduler.
54    fn sleep(&self);
55
56    /// Run a function in an atomic state w.r.t. to the current core. This
57    /// means that interrupts are disabled so that an interrupt will not fire
58    /// during the passed in function's execution, but *does not* make any
59    /// guarantees about memory consistency on a multi-core system.
60    unsafe fn with_interrupts_disabled<F, R>(&self, f: F) -> R
61    where
62        F: FnOnce() -> R;
63
64    /// Print out debug information about the current chip state (system
65    /// registers, MPU configuration, etc.) to a supplied writer.
66    ///
67    /// This function may be called across thread boundaries (such as from a
68    /// panic handler). As implementors of `Chip` do not have to be `Send` or
69    /// `Sync`, `&self` may not be available in these contexts. Therefore, this
70    /// function instead accepts an `Option<&Self>` parameter named `this`. In
71    /// contexts where `&self` is available, callers should invoke this function
72    /// by passing `Some(&self)` to `this`. Otherwise, `this` will be set to
73    /// `None`. The implementation of `print_state` may not print certain
74    /// information if it depends on runtime-accessible state in `Self`, but
75    /// that reference is not provided.
76    unsafe fn print_state(this: Option<&Self>, writer: &mut dyn Write);
77}
78
79/// Interface for retrieving the currently executing thread.
80///
81/// This is used to enforce correctness with shared state that has access
82/// restrictions (e.g., only a single thread can access a specific value).
83///
84/// Many embedded platforms are single-core and only permit a single execution
85/// thread at a time. However, interrupts can typically occur at any time, and
86/// the execution of an interrupt service routine (ISR) constitutes a second
87/// thread. Implementations of this trait must be able to differentiate between
88/// at minimum the main thread of execution and an ISR execution, but may also
89/// consider multiple execution threads if available on a particular device.
90///
91/// # Safety
92///
93/// This thread is marked as `unsafe` as implementation must guarantee its
94/// correctness. Users of this trait are allowed to make soundness guarantees
95/// based on the implementation being correct. Failing to provide a correct
96/// implementation can lead to unsound behavior. By implementing this trait,
97/// providers are guaranteeing the implementations are always correct for the
98/// given hardware platform.
99pub unsafe trait ThreadIdProvider {
100    /// Return a unique ID for the currently executing thread.
101    ///
102    /// The unique ID must fit in a `usize` and must be unique and consistent
103    /// for the currently running thread. The actual value is opaque and there
104    /// is no assumption about the meaning of the assigned IDs. Implementations
105    /// are allowed to arbitrarily assign IDs to threads as long as the IDs are
106    /// unique and consistent.
107    fn running_thread_id() -> usize;
108}
109
110/// Interface for handling interrupts on a hardware chip.
111///
112/// Each board must construct an implementation of this trait to handle specific
113/// interrupts. When an interrupt (identified by number) has triggered and
114/// should be handled, the implementation of this trait will be called with the
115/// interrupt number. The implementation can then handle the interrupt, or
116/// return `false` to signify that it does not know how to handle the interrupt.
117///
118/// This functionality is given this `InterruptService` interface so that
119/// multiple objects can be chained together to handle interrupts for a chip.
120/// This is useful for code organization and removing the need for duplication
121/// when multiple variations of a specific microcontroller exist. Then a shared,
122/// base object can handle most interrupts, and variation-specific objects can
123/// handle the variation-specific interrupts.
124///
125/// To simplify structuring the Rust code when using `InterruptService`, the
126/// interrupt number should be passed "top-down". That is, an interrupt to be
127/// handled will first be passed to the `InterruptService` object that is most
128/// specific. If that object cannot handle the interrupt, then it should
129/// maintain a reference to the second most specific object, and return by
130/// calling to that object to handle the interrupt. This continues until the
131/// base object handles the interrupt or decides that the chip does not know how
132/// to handle the interrupt. For example, consider a `nRF52840` chip that
133/// depends on the `nRF52` crate. If both have specific interrupts they know how
134/// to handle, the flow would look like:
135///
136/// ```ignore
137///           +---->nrf52840_peripherals
138///           |        |
139///           |        |
140///           |        v
141/// kernel-->nrf52     nrf52_peripherals
142/// ```
143/// where the kernel instructs the `nrf52` crate to handle interrupts, and if
144/// there is an interrupt ready then that interrupt is passed through the
145/// InterruptService objects until something can service it.
146pub trait InterruptService {
147    /// Service an interrupt, if supported by this chip. If this interrupt
148    /// number is not supported, return false.
149    unsafe fn service_interrupt(&self, interrupt: u32) -> bool;
150}
151
152/// A default implementation of `InterruptService` that handles nothing and returns `false`.
153impl InterruptService for () {
154    unsafe fn service_interrupt(&self, _interrupt: u32) -> bool {
155        false
156    }
157}
158
159/// Generic operations that clock-like things are expected to support.
160pub trait ClockInterface {
161    fn is_enabled(&self) -> bool;
162    fn enable(&self);
163    fn disable(&self);
164}
165
166/// Helper struct for interfaces that expect clocks, but have no clock control.
167pub struct NoClockControl {}
168impl ClockInterface for NoClockControl {
169    fn is_enabled(&self) -> bool {
170        true
171    }
172    fn enable(&self) {}
173    fn disable(&self) {}
174}
175
176/// Instance of NoClockControl for things that need references to
177/// `ClockInterface` objects.
178pub const NO_CLOCK_CONTROL: NoClockControl = NoClockControl {};