1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
//! Target side implementation of the RTT (Real-Time Transfer) I/O protocol
//!
//! RTT implements input and output to/from a debug probe using in-memory ring buffers and memory
//! polling. This enables debug logging from the microcontroller with minimal delays and no
//! blocking, making it usable even in real-time applications where e.g. semihosting delays cannot
//! be tolerated.
//!
//! # Hardware support
//!
//! This crate is platform agnostic and can be used on any chip that supports background memory
//! access via its debug interface. The printing macros require a critical section which is
//! platform-dependent. Built-in ARM Cortex-M support can be enabled with the "cortex-m" feature,
//! and RISC-V support can be enabled with the "riscv" feature.
//!
//! To interface with RTT from the host computer, a debug probe such as an ST-Link or J-Link is
//! required. The normal debug protocol (e.g. SWD) is used to access RTT, so no extra connections
//! such as SWO pins are needed.
//!
//! # Initialization
//!
//! RTT must be initialized at the start of your program using one of the init macros. See the
//! macros for more details.
//!
//! The initialization macros return channel objects that can be used for writing and reading.
//! Different channel objects can safely be used concurrently in different contexts without locking.
//! In an interrupt-based application with realtime constraints you could use a separate channel for
//! every interrupt context to allow for lock-free logging.
//!
//! # Channels and virtual terminals
//!
//! RTT supports multiple *channels* in both directions. Up channels go from target to host, and
//! down channels go from host to target. Each channel is identified by its direction and number.
//!
//! By convention channel 0 is reserved for terminal use. In the up direction there is a set of
//! escape sequences that further enable the single channel to be treated as up to 16 *virtual
//! terminals*. This can be used to separate different types of messages (for example, log levels)
//! from each other without having to allocate memory for multiple buffers. As a downside, multiple
//! threads cannot write to the same channel at once, even if using different virtual terminal
//! numbers, so access has to be synchronized. Down channel 0 is conventionally used for keyboard
//! input.
//!
//! **Note:** Some host side programs only display channel 0 by default, so to see the other
//! channels you might need to configure them appropriately.
//!
//! The other channels can be used to either enable concurrent use from multiple sources without
//! locking, or to send e.g. binary data in either direction.
//!
//! Channel 0 can also be used for arbitrary data, but most tools expect it to be plain text.
//!
//! # Channel modes
//!
//! By default, channels start in [`NoBlockSkip`](ChannelMode::NoBlockSkip) mode, which discards
//! data if the buffer is full. This enables RTT to not crash the application if there is no debug
//! probe attached or if the host is not reading the buffers. However if the application outputs
//! faster than the host can read (which is easy to do, because writing is very fast), messages will
//! be lost. Channels can be set to blocking mode if this is desirable, however in that case the
//! application will likely freeze when the buffer fills up if a debugger is not attached.
//!
//! The channel mode can also be changed on the fly by the debug probe. Therefore it may be
//! advantageous to use a non-blocking mode in your microcontroller code, and set a blocking mode as
//! needed when debugging. That way you will never end up with an application that freezes without a
//! debugger connected.
//!
//! # Printing
//!
//! For no-hassle output the [`rprint`] and [`rprintln`] macros are provided. They use a single down
//! channel defined at initialization time, and a critical section for synchronization, and they
//! therefore work exactly like the standard `println` style macros. They can be used from any
//! context. The [`rtt_init_print`] convenience macro initializes printing on channel 0.
//!
//! ```
//! use rtt_target::{rtt_init_print, rprintln};
//!
//! fn main() -> ! {
//!     rtt_init_print!();
//!     loop {
//!         rprintln!("Hello, world!");
//!     }
//! }
//! ```
//!
//! The macros also support an extended syntax to print to different RTT virtual terminals.
//!
//! Please note that because a critical section is used, printing into a blocking channel will cause
//! the application to block and freeze when the buffer is full.

#![no_std]

use core::convert::Infallible;
use core::fmt;
use core::mem::MaybeUninit;
use ufmt_write::uWrite;

#[macro_use]
mod init;

/// Public due to access from macro
#[doc(hidden)]
pub mod rtt;

#[macro_use]
mod print;

pub use print::*;

/// RTT up (target to host) channel
///
/// Supports writing binary data directly, or writing strings via [`core::fmt`] macros such as
/// [`write`] as well as the ufmt crate's `uwrite` macros (use the `u` method).
///
/// Note that the formatted writing implementations diverge slightly from the trait definitions in
/// that if the channel is in non-blocking mode, writing will *not* block.
pub struct UpChannel(*mut rtt::RttChannel);

unsafe impl Send for UpChannel {}

impl UpChannel {
    /// Public due to access from macro.
    #[doc(hidden)]
    pub unsafe fn new(channel: *mut rtt::RttChannel) -> Self {
        UpChannel(channel)
    }

    fn channel(&self) -> &mut rtt::RttChannel {
        unsafe { &mut *self.0 }
    }

    /// Writes `buf` to the channel and returns the number of bytes written. Behavior when the
    /// buffer is full is subject to the channel blocking mode.
    pub fn write(&mut self, buf: &[u8]) -> usize {
        let mut writer = self.channel().writer();
        writer.write(buf);
        writer.commit()
    }

    /// Creates a writer for formatted writing with ufmt.
    ///
    /// The correct way to use this method is to call it once for each write operation. This is so
    /// that non blocking modes will work correctly.
    ///
    /// ```
    /// let mut output = channels.up.0;
    /// uwriteln!(output.u(), "Hello, ufmt!");
    /// ```
    pub fn u(&mut self) -> uWriter {
        uWriter(self.channel().writer())
    }

    /// Gets the current blocking mode of the channel. The default is `NoBlockSkip`.
    pub fn mode(&self) -> ChannelMode {
        self.channel().mode()
    }

    /// Sets the blocking mode of the channel
    pub fn set_mode(&mut self, mode: ChannelMode) {
        self.channel().set_mode(mode)
    }

    /// Converts the channel into a virtual terminal that can be used for writing into multiple
    /// virtual terminals.
    pub fn into_terminal(self) -> TerminalChannel {
        TerminalChannel::new(self)
    }

    /// Magically creates a channel out of thin air. Return `None` if the channel number is too
    /// high, or if the channel has not been initialized.
    ///
    /// Calling this function will cause a linking error if `rtt_init` has not been called.
    ///
    /// # Safety
    ///
    /// It's undefined behavior for something else to access the channel through anything else
    /// besides the returned object during or after calling this function. Essentially this function
    /// is only safe to use in panic handlers and the like that permanently disable interrupts.
    pub unsafe fn conjure(number: usize) -> Option<UpChannel> {
        extern "C" {
            #[link_name = "_SEGGER_RTT"]
            static mut CONTROL_BLOCK: MaybeUninit<rtt::RttHeader>;
        }

        if number >= (&*CONTROL_BLOCK.as_ptr()).max_up_channels() {
            return None;
        }

        // First addition moves to the start of the up channel array, second addition moves to the
        // correct channel.
        let ptr = (CONTROL_BLOCK.as_ptr().add(1) as *mut rtt::RttChannel).add(number);

        if !(&*ptr).is_initialized() {
            return None;
        }

        Some(UpChannel(ptr))
    }
}

impl fmt::Write for UpChannel {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        self.channel().writer().write_str(s)
    }

    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), fmt::Error> {
        self.channel().writer().write_fmt(args)
    }
}

/// Writer for ufmt. Don't store an instance of this, but rather create a new one for every write.
#[allow(non_camel_case_types)]
pub struct uWriter<'c>(rtt::RttWriter<'c>);

impl uWrite for uWriter<'_> {
    type Error = Infallible;

    fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
        self.0.write(s.as_bytes());
        Ok(())
    }
}

/// RTT down (host to target) channel
pub struct DownChannel(*mut rtt::RttChannel);

unsafe impl Send for DownChannel {}

impl DownChannel {
    /// Public due to access from macro.
    #[doc(hidden)]
    pub unsafe fn new(channel: *mut rtt::RttChannel) -> Self {
        DownChannel(channel)
    }

    fn channel(&mut self) -> &mut rtt::RttChannel {
        unsafe { &mut *self.0 }
    }

    /// Reads up to `buf.len()` bytes from the channel and return the number of bytes read. This
    /// method never blocks.
    pub fn read(&mut self, buf: &mut [u8]) -> usize {
        self.channel().read(buf)
    }
}

/// Specifies what to do when a channel doesn't have enough buffer space for a complete write.
#[derive(Eq, PartialEq)]
#[repr(usize)]
pub enum ChannelMode {
    /// Skip writing the data completely if it doesn't fit in its entirety.
    NoBlockSkip = 0,

    /// Write as much as possible of the data and ignore the rest.
    NoBlockTrim = 1,

    /// Block (spin) if the buffer is full. If within a critical section such as inside
    /// [`rprintln`], this will cause the application to freeze until the host reads from the
    /// buffer.
    BlockIfFull = 2,
}

/// An up channel that supports writing into multiple virtual terminals within the same buffer.
///
/// An [`UpChannel`] can be turned into a `TerminalChannel` by using the
/// [`into_terminal`](UpChannel::into_terminal()) method.
///
/// Virtual terminals allow you to share one buffer for writing multiple streams. The virtual
/// terminals number from 0 to 15 and are implemented with a simple "terminal switch" sequence on
/// the fly, so there is no need to declare them in advance. You could, for example, use different
/// terminal numbers for messages of different priorities to separate them in a viewer program.
/// Printing uses a `TerminalChannel` internally.
pub struct TerminalChannel {
    channel: UpChannel,
    current: u8,
}

impl TerminalChannel {
    pub(crate) fn new(channel: UpChannel) -> Self {
        Self {
            channel,
            current: 0,
        }
    }

    /// Creates a writer to write a message to the virtual terminal specified by `number`.
    ///
    /// The correct way to use this method is to call it once for each write operation. This is so
    /// that non blocking modes will work correctly.
    ///
    /// The writer supports formatted writing with the standard `write` and ufmt's `uwrite`.
    pub fn write(&mut self, number: u8) -> TerminalWriter {
        const TERMINAL_ID: [u8; 16] = *b"0123456789ABCDEF";

        let mut writer = self.channel.channel().writer();

        if number != self.current {
            // The terminal switch command must be sent in full so the mode cannot be NoBlockTrim
            let mode = self.channel.mode();
            let mode = if mode == ChannelMode::NoBlockTrim {
                ChannelMode::NoBlockSkip
            } else {
                mode
            };

            writer.write_with_mode(mode, &[0xff, TERMINAL_ID[(number & 0x0f) as usize]]);

            self.current = number;
        }

        TerminalWriter {
            writer,
            number,
            current: &mut self.current,
        }
    }

    /// Gets the current blocking mode of the channel. The default is `NoBlockSkip`.
    pub fn mode(&self) -> ChannelMode {
        self.channel.mode()
    }

    /// Sets the blocking mode of the channel
    pub fn set_mode(&mut self, mode: ChannelMode) {
        self.channel.set_mode(mode)
    }
}

/// Formatted writing operation. Don't store an instance of this, but rather create a new one for
/// every write.
pub struct TerminalWriter<'c> {
    writer: rtt::RttWriter<'c>,
    number: u8,
    current: &'c mut u8,
}

impl fmt::Write for TerminalWriter<'_> {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        self.writer.write(s.as_bytes());
        Ok(())
    }
}

impl uWrite for TerminalWriter<'_> {
    type Error = Infallible;

    fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
        self.writer.write(s.as_bytes());
        Ok(())
    }
}

impl Drop for TerminalWriter<'_> {
    fn drop(&mut self) {
        if !self.writer.is_failed() {
            *self.current = self.number;
        }
    }
}