Skip to main content

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