segger/
rtt.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//! Segger RTT implementation.
6//!
7//! RTT is a protocol for sending debugging messages to a connected host. The
8//! embedded platform configures a portion of memory in a special way, and then
9//! the host uses a JTAG connection to read the messages out of the chip's
10//! memory.
11//!
12//! Receiving RTT Messages
13//! ----------------------
14//!
15//! With the jlink tools, receiving RTT messages is a two step process. First,
16//! open a JTAG connection with a command like:
17//!
18//! ```shell
19//! $ JLinkExe -device nrf52 -if swd -speed 1000 -autoconnect 1
20//! ```
21//!
22//! Then, use the `JLinkRTTClient` tool in a different terminal to print the
23//! messages:
24//!
25//! ```shell
26//! $ JLinkRTTClient
27//! ```
28//!
29//! Todo
30//! ----
31//!
32//! - Implement receive functionality.
33//!
34//! Usage
35//! -----
36//!
37//! ```rust,ignore
38//! pub struct Platform {
39//!     // Other fields omitted for clarity
40//!     console: &'static capsules::console::Console<'static>,
41//! }
42//! ```
43//!
44//! In `main()`:
45//!
46//! ```rust,ignore
47//! # use kernel::static_init;
48//! # use capsules::virtual_alarm::VirtualMuxAlarm;
49//!
50//! let virtual_alarm_rtt = static_init!(
51//!     VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>,
52//!     VirtualMuxAlarm::new(mux_alarm)
53//! );
54//! virtual_alarm_rtt.setup();
55//!
56//! let rtt_memory = static_init!(
57//!     capsules::segger_rtt::SeggerRttMemory,
58//!     capsules::segger_rtt::SeggerRttMemory::new(b"Terminal\0",
59//!         &mut capsules::segger_rtt::UP_BUFFER,
60//!         b"Terminal\0",
61//!         &mut capsules::segger_rtt::DOWN_BUFFER)
62//! );
63//!
64//! let rtt = static_init!(
65//!     capsules::segger_rtt::SeggerRtt<VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>>,
66//!     capsules::segger_rtt::SeggerRtt::new(virtual_alarm_rtt, rtt_memory,
67//!         &mut capsules::segger_rtt::UP_BUFFER,
68//!         &mut capsules::segger_rtt::DOWN_BUFFER)
69//! );
70//! virtual_alarm_rtt.set_client(rtt);
71//!
72//! let console = static_init!(
73//!     capsules::console::Console<'static>,
74//!     capsules::console::Console::new(
75//!         rtt,
76//!         &mut capsules::console::WRITE_BUF,
77//!         &mut capsules::console::READ_BUF,
78//!         board_kernel.create_grant(&grant_cap)
79//!     )
80//! );
81//! kernel::hil::uart::UART::set_client(rtt, console);
82//! console.initialize();
83//! ```
84
85use core::cell::Cell;
86use core::marker::PhantomData;
87use core::ops::Index;
88use core::sync::atomic::{fence, Ordering};
89use kernel::hil;
90use kernel::hil::time::ConvertTicks;
91use kernel::hil::uart;
92use kernel::utilities::cells::{OptionalCell, TakeCell, VolatileCell};
93use kernel::ErrorCode;
94
95/// Suggested length for the up buffer to pass to the Segger RTT capsule.
96pub const DEFAULT_UP_BUFFER_LENGTH: usize = 1024;
97
98/// Suggested length for the down buffer to pass to the Segger RTT capsule.
99pub const DEFAULT_DOWN_BUFFER_LENGTH: usize = 32;
100
101/// Milliseconds to wait to flush tx buffer after writing
102const TX_MS_DELAY: u32 = 1;
103
104/// Milliseconds to wait between checking if rx data is available
105const RX_MS_DELAY: u32 = 100;
106
107/// This structure is defined by the segger RTT protocol.
108///
109/// It must exist in memory in exactly this form so that the segger
110/// JTAG tool can find it in the chip's memory and read and write
111/// messages to the appropriate buffers.
112#[repr(C)]
113pub struct SeggerRttMemory<'a> {
114    id: [u8; 16],
115    number_up_buffers: u32,
116    number_down_buffers: u32,
117    up_buffer: SeggerRttBuffer<'a>,
118    down_buffer: SeggerRttBuffer<'a>,
119}
120
121#[repr(C)]
122pub struct SeggerRttBuffer<'a> {
123    name: *const u8, // Pointer to the name of this channel. Must be a 4 byte thin pointer.
124    // These fields are marked as `pub` to allow access in the panic handler.
125    pub buffer: *const VolatileCell<u8>, // Pointer to the buffer for this channel.
126    pub length: u32,
127    pub write_position: VolatileCell<u32>,
128    read_position: VolatileCell<u32>,
129    flags: u32,
130    _lifetime: PhantomData<&'a ()>,
131}
132
133impl Index<usize> for SeggerRttBuffer<'_> {
134    type Output = VolatileCell<u8>;
135
136    fn index(&self, index: usize) -> &Self::Output {
137        let index = index as isize;
138        if index >= self.length as isize {
139            panic!("Index out of bounds {}/{}", index, self.length)
140        } else {
141            unsafe { &*self.buffer.offset(index) }
142        }
143    }
144}
145
146impl<'a> SeggerRttMemory<'a> {
147    pub fn new_raw(
148        up_buffer_name: &'a [u8],
149        up_buffer: &'a [VolatileCell<u8>],
150        down_buffer_name: &'a [u8],
151        down_buffer: &'a [VolatileCell<u8>],
152    ) -> SeggerRttMemory<'a> {
153        SeggerRttMemory {
154            // This field is a magic value that must be set to "SEGGER RTT" for the debugger to
155            // recognize it when scanning the memory.
156            //
157            // In principle, there could be a risk that the value is duplicated elsewhere in
158            // memory, therefore confusing the debugger. However in practice this hasn't caused any
159            // known problem so far. If needed, this ID could be scrambled here, with the real magic
160            // value being written only when this object is fully initialized.
161            id: *b"SEGGER RTT\0\0\0\0\0\0",
162            number_up_buffers: 1,
163            number_down_buffers: 1,
164            up_buffer: SeggerRttBuffer {
165                name: up_buffer_name.as_ptr(),
166                buffer: up_buffer.as_ptr(),
167                length: up_buffer.len() as u32,
168                write_position: VolatileCell::new(0),
169                read_position: VolatileCell::new(0),
170                flags: 0,
171                _lifetime: PhantomData,
172            },
173            down_buffer: SeggerRttBuffer {
174                name: down_buffer_name.as_ptr(),
175                buffer: down_buffer.as_ptr(),
176                length: down_buffer.len() as u32,
177                write_position: VolatileCell::new(0),
178                read_position: VolatileCell::new(0),
179                flags: 0,
180                _lifetime: PhantomData,
181            },
182        }
183    }
184
185    /// This getter allows access to the underlying buffer in the panic handler.
186    /// The result is a pointer so that only `unsafe` code can actually dereference it - this is to
187    /// restrict this priviledged access to the panic handler.
188    pub fn get_up_buffer_ptr(&self) -> *const SeggerRttBuffer<'a> {
189        &self.up_buffer
190    }
191
192    pub fn write_sync(&self, buf: &[u8]) {
193        let mut index = self.up_buffer.write_position.get() as usize;
194        fence(Ordering::SeqCst);
195
196        let buffer_len = self.up_buffer.length as usize;
197        for c in buf.iter() {
198            index = (index + 1) % buffer_len;
199            while self.up_buffer.read_position.get() as usize == index {
200                core::hint::spin_loop();
201            }
202            self.up_buffer[index].set(*c);
203            fence(Ordering::SeqCst);
204            self.up_buffer.write_position.set(index as u32);
205            fence(Ordering::SeqCst);
206        }
207    }
208}
209
210pub struct SeggerRtt<'a, A: hil::time::Alarm<'a>> {
211    alarm: &'a A, // Dummy alarm so we can get a callback.
212    config: TakeCell<'a, SeggerRttMemory<'a>>,
213    tx_client: OptionalCell<&'a dyn uart::TransmitClient>,
214    tx_client_buffer: TakeCell<'static, [u8]>,
215    tx_len: Cell<usize>,
216    rx_client: OptionalCell<&'a dyn uart::ReceiveClient>,
217    rx_client_buffer: TakeCell<'static, [u8]>,
218    rx_cursor: Cell<usize>,
219    rx_len: Cell<usize>,
220}
221
222impl<'a, A: hil::time::Alarm<'a>> SeggerRtt<'a, A> {
223    pub fn new(alarm: &'a A, config: &'a mut SeggerRttMemory<'a>) -> SeggerRtt<'a, A> {
224        SeggerRtt {
225            alarm,
226            config: TakeCell::new(config),
227            tx_client: OptionalCell::empty(),
228            tx_client_buffer: TakeCell::empty(),
229            tx_len: Cell::new(0),
230            rx_client: OptionalCell::empty(),
231            rx_client_buffer: TakeCell::empty(),
232            rx_cursor: Cell::new(0),
233            rx_len: Cell::new(0),
234        }
235    }
236}
237
238impl<'a, A: hil::time::Alarm<'a>> uart::Transmit<'a> for SeggerRtt<'a, A> {
239    fn set_transmit_client(&self, client: &'a dyn uart::TransmitClient) {
240        self.tx_client.set(client);
241    }
242
243    fn transmit_buffer(
244        &self,
245        tx_data: &'static mut [u8],
246        tx_len: usize,
247    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
248        if self.config.is_some() {
249            self.config.map(|config| {
250                // Copy the incoming data into the buffer. Once we increment
251                // the `write_position` the RTT listener will go ahead and read
252                // the message from us.
253                let mut index = config.up_buffer.write_position.get() as usize;
254                fence(Ordering::SeqCst);
255
256                let buffer_len = config.up_buffer.length as usize;
257                for i in 0..tx_len {
258                    config.up_buffer[(i + index) % buffer_len].set(tx_data[i]);
259                }
260                fence(Ordering::SeqCst);
261
262                index = (index + tx_len) % buffer_len;
263                config.up_buffer.write_position.set(index as u32);
264                fence(Ordering::SeqCst);
265
266                self.tx_len.set(tx_len);
267                // Save the client buffer so we can pass it back with the callback.
268                self.tx_client_buffer.replace(tx_data);
269
270                // Start a short timer so that we get a callback and can issue the callback to
271                // the client.
272                //
273                // This heuristic interval was tested with the console capsule on a nRF52840-DK
274                // board, passing buffers up to 1500 bytes from userspace. 100 micro-seconds
275                // was too short, even for buffers as small as 128 bytes. 1 milli-second seems to
276                // be reliable.
277                let delay = self.alarm.ticks_from_ms(TX_MS_DELAY);
278                self.alarm.set_alarm(self.alarm.now(), delay);
279            });
280            Ok(())
281        } else {
282            Err((ErrorCode::BUSY, tx_data))
283        }
284    }
285
286    fn transmit_word(&self, _word: u32) -> Result<(), ErrorCode> {
287        Err(ErrorCode::FAIL)
288    }
289
290    fn transmit_abort(&self) -> Result<(), ErrorCode> {
291        Ok(())
292    }
293}
294
295impl<'a, A: hil::time::Alarm<'a>> hil::time::AlarmClient for SeggerRtt<'a, A> {
296    fn alarm(&self) {
297        self.tx_client.map(|client| {
298            self.tx_client_buffer.take().map(|buffer| {
299                client.transmitted_buffer(buffer, self.tx_len.get(), Ok(()));
300            });
301        });
302        self.rx_client.map(|client| {
303            self.rx_client_buffer.take().map(|buffer| {
304                self.config.map(|config| {
305                    let write_position = &config.down_buffer.write_position;
306                    let read_position = &config.down_buffer.read_position;
307
308                    // ensure all reads/writes to position data has already happened
309                    fence(Ordering::SeqCst);
310                    while self.rx_cursor.get() < self.rx_len.get()
311                        && write_position.get() != read_position.get()
312                    {
313                        buffer[self.rx_cursor.get()] =
314                            config.down_buffer[read_position.get() as usize].get();
315                        // ensure output data ordered before updating read_position
316                        fence(Ordering::SeqCst);
317                        read_position.set((read_position.get() + 1) % config.down_buffer.length);
318                        self.rx_cursor.set(self.rx_cursor.get() + 1);
319                    }
320                    // "flush" the final rx_cursor update
321                    fence(Ordering::SeqCst);
322                });
323                if self.rx_cursor.get() == self.rx_len.get() {
324                    client.received_buffer(buffer, self.rx_len.get(), Ok(()), uart::Error::None);
325                } else {
326                    let delay = self.alarm.ticks_from_ms(RX_MS_DELAY);
327                    self.alarm.set_alarm(self.alarm.now(), delay);
328                    self.rx_client_buffer.put(Some(buffer))
329                }
330            });
331        });
332    }
333}
334
335// Dummy implementation so this can act as the underlying UART for a
336// virtualized UART MUX. -pal 1/10/19
337impl<'a, A: hil::time::Alarm<'a>> uart::Configure for SeggerRtt<'a, A> {
338    fn configure(&self, _parameters: uart::Parameters) -> Result<(), ErrorCode> {
339        Err(ErrorCode::FAIL)
340    }
341}
342
343impl<'a, A: hil::time::Alarm<'a>> uart::Receive<'a> for SeggerRtt<'a, A> {
344    fn set_receive_client(&self, client: &'a dyn uart::ReceiveClient) {
345        self.rx_client.set(client)
346    }
347
348    fn receive_buffer(
349        &self,
350        buffer: &'static mut [u8],
351        len: usize,
352    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
353        self.rx_client_buffer.put(Some(buffer));
354        self.rx_len.set(len);
355        self.rx_cursor.set(0);
356        if !self.alarm.is_armed() {
357            let delay = self.alarm.ticks_from_ms(RX_MS_DELAY);
358            self.alarm.set_alarm(self.alarm.now(), delay);
359        }
360        Ok(())
361    }
362
363    fn receive_word(&self) -> Result<(), ErrorCode> {
364        Err(ErrorCode::FAIL)
365    }
366
367    fn receive_abort(&self) -> Result<(), ErrorCode> {
368        Ok(())
369    }
370}