components/
debug_writer.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//! Component for DebugWriter, the implementation for `debug!()`.
6//!
7//! This provides components for attaching the kernel debug output (for panic!,
8//! print!, debug!, etc.) to the output. `DebugWriterComponent` uses a UART mux,
9//! and `DebugWriterNoMuxComponent` just uses a UART interface directly.
10//!
11//! Usage
12//! -----
13//! ```rust
14//! let debug_wrapper = components::debug_writer::DebugWriterComponent::new(
15//!     uart_mux,
16//!     create_capability!(kernel::capabilities::SetDebugWriterCapability),
17//! )
18//! .finalize(components::debug_writer_component_static!());
19//!
20//! let debug_wrapper = components::debug_writer::DebugWriterNoMuxComponent::new(
21//!     &nrf52::uart::UARTE0,
22//!     create_capability!(kernel::capabilities::SetDebugWriterCapability),
23//! )
24//! .finalize(components::debug_writer_no_mux_component_static!());
25//! ```
26
27// Author: Brad Campbell <bradjc@virginia.edu>
28// Last modified: 11/07/2019
29
30use capsules_core::virtualizers::virtual_uart::{MuxUart, UartDevice};
31use capsules_system::debug_writer::uart_debug_writer::UartDebugWriter;
32use core::mem::MaybeUninit;
33use kernel::capabilities;
34use kernel::capabilities::SetDebugWriterCapability;
35use kernel::collections::ring_buffer::RingBuffer;
36use kernel::component::Component;
37use kernel::hil;
38use kernel::hil::uart;
39
40// The sum of the output_buf and internal_buf is set to a multiple of 1024 bytes in order to avoid excessive
41// padding between kernel memory and application memory (which often needs to be aligned to at
42// least a 1 KiB boundary). This is not _semantically_ critical, but helps keep buffers on 1 KiB
43// boundaries in some cases. Of course, these definitions are only advisory, and individual boards
44// can choose to pass in their own buffers with different lengths.
45pub const DEFAULT_DEBUG_BUFFER_KBYTE: usize = 2;
46
47// Bytes [0, DEBUG_BUFFER_SPLIT) are used for output_buf while bytes
48// [DEBUG_BUFFER_SPLIT, DEFAULT_DEBUG_BUFFER_KBYTE * 1024) are used for internal_buf.
49const DEBUG_BUFFER_SPLIT: usize = 64;
50
51/// The optional argument to this macro allows boards to specify the size of the in-RAM
52/// buffer used for storing debug messages.
53///
54/// Increase this value to be able to send more debug messages in
55/// quick succession.
56#[macro_export]
57macro_rules! debug_writer_component_static {
58    ($BUF_SIZE_KB:expr) => {{
59        let uart = kernel::static_buf!(capsules_core::virtualizers::virtual_uart::UartDevice);
60        let ring = kernel::static_buf!(kernel::collections::ring_buffer::RingBuffer<'static, u8>);
61        let buffer = kernel::static_buf!([u8; 1024 * $BUF_SIZE_KB]);
62        let debug =
63            kernel::static_buf!(capsules_system::debug_writer::uart_debug_writer::UartDebugWriter);
64
65        (uart, ring, buffer, debug)
66    };};
67    () => {{
68        $crate::debug_writer_component_static!($crate::debug_writer::DEFAULT_DEBUG_BUFFER_KBYTE)
69    };};
70}
71
72/// The optional argument to this macro allows boards to specify the size of the in-RAM
73/// buffer used for storing debug messages.
74///
75/// Increase this value to be able to send more debug messages in
76/// quick succession.
77#[macro_export]
78macro_rules! debug_writer_no_mux_component_static {
79    ($BUF_SIZE_KB:expr) => {{
80        let ring = kernel::static_buf!(kernel::collections::ring_buffer::RingBuffer<'static, u8>);
81        let buffer = kernel::static_buf!([u8; 1024 * $BUF_SIZE_KB]);
82        let debug =
83            kernel::static_buf!(capsules_system::debug_writer::uart_debug_writer::UartDebugWriter);
84
85        (ring, buffer, debug)
86    };};
87    () => {{
88        use $crate::debug_writer::DEFAULT_DEBUG_BUFFER_KBYTE;
89        $crate::debug_writer_no_mux_component_static!(DEFAULT_DEBUG_BUFFER_KBYTE)
90    };};
91}
92
93// Allow dead code because we need the `Chip` type but don't use `chip`.
94#[allow(dead_code)]
95pub struct DebugWriterComponent<const BUF_SIZE_BYTES: usize, C: SetDebugWriterCapability> {
96    uart_mux: &'static MuxUart<'static>,
97    marker: core::marker::PhantomData<[u8; BUF_SIZE_BYTES]>,
98    capability: C,
99}
100
101impl<const BUF_SIZE_BYTES: usize, C: SetDebugWriterCapability>
102    DebugWriterComponent<BUF_SIZE_BYTES, C>
103{
104    /// Create a debug writer component while binding the global variable used
105    /// by debug.rs to the main thread.
106    #[cfg(target_has_atomic = "ptr")]
107    pub fn new<P: kernel::platform::chip::ThreadIdProvider>(
108        uart_mux: &'static MuxUart,
109        capability: C,
110    ) -> Self {
111        kernel::debug::initialize_debug_writer_wrapper::<P>();
112
113        Self {
114            uart_mux,
115            marker: core::marker::PhantomData,
116            capability,
117        }
118    }
119
120    /// Create a debug writer component and bind the global variable used by
121    /// debug.rs to the main thread, but require that the caller(i.e., main.rs)
122    /// provides the actual init call.
123    ///
124    /// This allows moving the unsafe call to main.rs instead of being
125    /// encapsulated in a component.
126    ///
127    /// The resulting use of this component for platforms without atomics
128    /// support looks like this:
129    ///
130    /// ```
131    ///  components::debug_writer::DebugWriterComponent::new(
132    ///      ...,
133    ///      || unsafe {
134    ///         kernel::debug::initialize_debug_writer_wrapper_unsafe::<
135    ///             <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
136    ///         >();
137    ///      })
138    /// .finalize(components::debug_writer_component_static!());
139    /// ```
140    pub fn new_unsafe<F>(uart_mux: &'static MuxUart, capability: C, bind_debug_global: F) -> Self
141    where
142        F: FnOnce(),
143    {
144        bind_debug_global();
145
146        Self {
147            uart_mux,
148            marker: core::marker::PhantomData,
149            capability,
150        }
151    }
152}
153
154pub struct Capability;
155unsafe impl capabilities::ProcessManagementCapability for Capability {}
156
157impl<const BUF_SIZE_BYTES: usize, C: SetDebugWriterCapability> Component
158    for DebugWriterComponent<BUF_SIZE_BYTES, C>
159{
160    type StaticInput = (
161        &'static mut MaybeUninit<UartDevice<'static>>,
162        &'static mut MaybeUninit<RingBuffer<'static, u8>>,
163        &'static mut MaybeUninit<[u8; BUF_SIZE_BYTES]>,
164        &'static mut MaybeUninit<UartDebugWriter>,
165    );
166    type Output = ();
167
168    fn finalize(self, s: Self::StaticInput) -> Self::Output {
169        let buf = s.2.write([0; BUF_SIZE_BYTES]);
170
171        let (output_buf, internal_buf) = buf.split_at_mut(DEBUG_BUFFER_SPLIT);
172
173        // Create virtual device for kernel debug.
174        let debugger_uart = s.0.write(UartDevice::new(self.uart_mux, false));
175        debugger_uart.setup();
176        let ring_buffer = s.1.write(RingBuffer::new(internal_buf));
177        let debugger =
178            s.3.write(UartDebugWriter::new(debugger_uart, output_buf, ring_buffer));
179        hil::uart::Transmit::set_transmit_client(debugger_uart, debugger);
180
181        kernel::debug::set_debug_writer_wrapper(debugger, self.capability);
182    }
183}
184
185// Allow dead code because we need the `Chip` type but don't use `chip`.
186#[allow(dead_code)]
187pub struct DebugWriterNoMuxComponent<
188    U: uart::Uart<'static> + uart::Transmit<'static> + 'static,
189    const BUF_SIZE_BYTES: usize,
190    C: SetDebugWriterCapability,
191> {
192    uart: &'static U,
193    marker: core::marker::PhantomData<[u8; BUF_SIZE_BYTES]>,
194    capability: C,
195}
196
197impl<
198        U: uart::Uart<'static> + uart::Transmit<'static> + 'static,
199        const BUF_SIZE_BYTES: usize,
200        C: SetDebugWriterCapability,
201    > DebugWriterNoMuxComponent<U, BUF_SIZE_BYTES, C>
202{
203    pub fn new(uart: &'static U, capability: C) -> Self {
204        Self {
205            uart,
206            marker: core::marker::PhantomData,
207            capability,
208        }
209    }
210}
211
212impl<
213        U: uart::Uart<'static> + uart::Transmit<'static> + 'static,
214        const BUF_SIZE_BYTES: usize,
215        C: SetDebugWriterCapability,
216    > Component for DebugWriterNoMuxComponent<U, BUF_SIZE_BYTES, C>
217{
218    type StaticInput = (
219        &'static mut MaybeUninit<RingBuffer<'static, u8>>,
220        &'static mut MaybeUninit<[u8; BUF_SIZE_BYTES]>,
221        &'static mut MaybeUninit<UartDebugWriter>,
222    );
223    type Output = ();
224
225    fn finalize(self, s: Self::StaticInput) -> Self::Output {
226        let buf = s.1.write([0; BUF_SIZE_BYTES]);
227        let (output_buf, internal_buf) = buf.split_at_mut(DEBUG_BUFFER_SPLIT);
228
229        // Create virtual device for kernel debug.
230        let ring_buffer = s.0.write(RingBuffer::new(internal_buf));
231        let debugger =
232            s.2.write(UartDebugWriter::new(self.uart, output_buf, ring_buffer));
233        hil::uart::Transmit::set_transmit_client(self.uart, debugger);
234
235        kernel::debug::set_debug_writer_wrapper(debugger, self.capability);
236
237        let _ = self.uart.configure(uart::Parameters {
238            baud_rate: 115200,
239            width: uart::Width::Eight,
240            stop_bits: uart::StopBits::One,
241            parity: uart::Parity::None,
242            hw_flow_control: false,
243        });
244    }
245}