├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples-cortex-m ├── .cargo │ └── config.toml ├── Cargo.toml ├── build.rs ├── memory.x └── src │ └── bin │ ├── custom.rs │ ├── defmt.rs │ ├── print.rs │ └── ufmt.rs ├── panic-rtt-target ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── panic-test ├── .cargo │ └── config.toml ├── Cargo.toml ├── build.rs ├── memory.x └── src │ └── main.rs └── rtt-target ├── Cargo.toml └── src ├── debug.rs ├── defmt.rs ├── init.rs ├── lib.rs ├── log.rs ├── print.rs └── rtt.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .cargo/config 3 | Cargo.lock 4 | test.sh 5 | scratch.txt 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["examples-cortex-m", "rtt-target", "panic-rtt-target", "panic-test"] 3 | resolver = "2" 4 | 5 | [patch.crates-io] 6 | rtt-target = { path = "./rtt-target" } 7 | panic-rtt-target = { path = "./panic-rtt-target" } 8 | 9 | # This is for the examples/tests 10 | [profile.release] 11 | codegen-units = 1 12 | lto = true 13 | opt-level = "s" 14 | panic = "abort" 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Matti Virkkunen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rtt-target 2 | 3 | [![crates.io](https://img.shields.io/crates/v/rtt-target.svg)](https://crates.io/crates/rtt-target) [![documentation](https://docs.rs/rtt-target/badge.svg)](https://docs.rs/rtt-target) 4 | 5 | Target side implementation of the RTT (Real-Time Transfer) I/O protocol. RTT implements input and output via a debug probe using in-memory ring buffers and 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. 6 | 7 | ## [Documentation](https://docs.rs/rtt-target) 8 | 9 | ## Platform support 10 | 11 | A platform-specific [`critical-section`](https://github.com/rust-embedded/critical-section) implementation is needed to use this library. 12 | 13 | Output directly to a channel object with `write!` or the binary `write` method does not require locking and therefore does not need any platform-specific critical section. 14 | 15 | ## Usage 16 | 17 | With a platform-specific critical section in use, printing is as simple as: 18 | 19 | ```rust 20 | use rtt_target::{rtt_init_print, rprintln}; 21 | 22 | fn main() { 23 | rtt_init_print!(); 24 | loop { 25 | rprintln!("Hello, world!"); 26 | } 27 | } 28 | ``` 29 | 30 | `rtt-target` also supports initializing multiple RTT channels, and even has a logger implementations 31 | for [`log`](https://docs.rs/log/latest/log/) and [`defmt`](https://defmt.ferrous-systems.com/) that can be used in conjunction with arbitrary 32 | channel setups. 33 | 34 | The `defmt` integration requires setting `features = ["defmt"]`. Furthermore, you have to either invoke `rtt_init_defmt!` or set up your channel(s) manually and invoke `set_defmt_channel` before using `defmt`. 35 | 36 | The `log` integration requires setting `features = ["log"]`. Furthermore, you have to either invoke `rtt_init_log!` or set up your channel(s) manually and invoke `init_logger`/`init_logger_with_level` before using `log`. 37 | 38 | **Note**: For your platform, particularly if you're using a multi-core MCU, external logger implementations might be better suited than the one provided by this crate via the `log`/`defmt` feature. 39 | 40 | For more information, please check out the [documentation](https://docs.rs/rtt-target). 41 | 42 | ## Development 43 | 44 | The examples-cortex-m and panic-test crates come with build files for the venerable STM32F103C8xx by default, but can be easily adapted for any chip as they contain only minimal platform-specific runtime code to get `fn main` to run. 45 | -------------------------------------------------------------------------------- /examples-cortex-m/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7m-none-eabi" 3 | 4 | [unstable] 5 | build-std = ["core"] 6 | -------------------------------------------------------------------------------- /examples-cortex-m/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-cortex-m" 3 | version = "0.1.0" 4 | authors = ["Matti Virkkunen "] 5 | edition = "2018" 6 | 7 | [features] 8 | defmt = ["dep:defmt", "rtt-target/defmt"] 9 | 10 | [dependencies] 11 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 12 | cortex-m-rt = "0.7" 13 | panic-halt = "0.2.0" 14 | rtt-target = { path = "../rtt-target" } 15 | ufmt = "0.2.0" 16 | defmt = { version = "0.3.0", optional = true } 17 | 18 | [[bin]] 19 | name = "custom" 20 | 21 | [[bin]] 22 | name = "print" 23 | 24 | [[bin]] 25 | name = "ufmt" 26 | 27 | [[bin]] 28 | name = "defmt" 29 | required-features = ["defmt"] 30 | -------------------------------------------------------------------------------- /examples-cortex-m/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, error::Error, fs::File, io::Write, path::PathBuf}; 2 | 3 | fn main() -> Result<(), Box> { 4 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 5 | 6 | println!("cargo:rustc-link-search={}", out_dir.display()); 7 | File::create(out_dir.join("memory.x"))?.write_all(include_bytes!("memory.x"))?; 8 | 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /examples-cortex-m/memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for building examples for STM32F103C8xx */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 64K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 20K 6 | } 7 | -------------------------------------------------------------------------------- /examples-cortex-m/src/bin/custom.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use cortex_m_rt::entry; 6 | use panic_halt as _; 7 | use rtt_target::{rtt_init, ChannelMode::BlockIfFull}; 8 | 9 | #[entry] 10 | fn main() -> ! { 11 | let channels = rtt_init! { 12 | up: { 13 | 0: { 14 | size: 512, 15 | mode: BlockIfFull, 16 | name: "Output zero" 17 | } 18 | 1: { 19 | size: 128, 20 | name: "Output one" 21 | } 22 | } 23 | down: { 24 | 0: { 25 | size: 512, 26 | mode: BlockIfFull, 27 | name: "Input zero" 28 | } 29 | } 30 | }; 31 | 32 | let mut output = channels.up.0; 33 | let mut input = channels.down.0; 34 | let mut buf = [0u8; 512]; 35 | 36 | let mut output2 = channels.up.1; 37 | writeln!( 38 | output2, 39 | "Hi! I will turn anything you type on channel 0 into upper case." 40 | ) 41 | .ok(); 42 | 43 | loop { 44 | let count = input.read(&mut buf[..]); 45 | if count > 0 { 46 | for c in buf.iter_mut() { 47 | c.make_ascii_uppercase(); 48 | } 49 | 50 | let mut p = 0; 51 | while p < count { 52 | p += output.write(&buf[p..count]); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples-cortex-m/src/bin/defmt.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use cortex_m_rt::entry; 5 | use panic_halt as _; 6 | use rtt_target::rtt_init_defmt; 7 | 8 | #[entry] 9 | fn main() -> ! { 10 | rtt_init_defmt!(); 11 | 12 | let mut i = 0; 13 | loop { 14 | defmt::println!("Loop {}...", i); 15 | 16 | i += 1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples-cortex-m/src/bin/print.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use cortex_m_rt::entry; 5 | use panic_halt as _; 6 | use rtt_target::{rprintln, rtt_init_print, ChannelMode::BlockIfFull}; 7 | 8 | #[entry] 9 | fn main() -> ! { 10 | rtt_init_print!(BlockIfFull); 11 | 12 | let mut i = 0; 13 | loop { 14 | rprintln!("Hello from RTT! {}", i); 15 | 16 | i += 1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples-cortex-m/src/bin/ufmt.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use cortex_m_rt::entry; 5 | use panic_halt as _; 6 | use rtt_target::{rtt_init_default, ChannelMode}; 7 | use ufmt::uwriteln; 8 | 9 | #[entry] 10 | fn main() -> ! { 11 | let channels = rtt_init_default!(); 12 | 13 | let mut output = channels.up.0; 14 | output.set_mode(ChannelMode::BlockIfFull); 15 | 16 | let mut i = 0; 17 | loop { 18 | uwriteln!(output.u(), "Hello from RTT! {}", i).ok(); 19 | 20 | i += 1; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /panic-rtt-target/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-rtt-target" 3 | description = "Logs panic messages over RTT using rtt-target" 4 | version = "0.2.0" 5 | edition = "2021" 6 | readme = "README.md" 7 | keywords = ["no-std", "embedded", "debugging", "rtt"] 8 | license = "MIT" 9 | authors = [ 10 | "Matti Virkkunen ", 11 | "Per Lindgren ", 12 | ] 13 | repository = "https://github.com/probe-rs/rtt-target" 14 | 15 | [dependencies] 16 | rtt-target = {version = "0.6.0", path = "../rtt-target" } 17 | critical-section = "1.1.1" 18 | portable-atomic = { version = "1.6.0", default-features = false } 19 | 20 | defmt = { version = "0.3.0", optional = true } 21 | 22 | [features] 23 | default = [] 24 | defmt = ["dep:defmt"] 25 | 26 | [package.metadata.docs.rs] 27 | features = ["defmt"] 28 | -------------------------------------------------------------------------------- /panic-rtt-target/README.md: -------------------------------------------------------------------------------- 1 | # panic-rtt-target 2 | 3 | [![crates.io](https://img.shields.io/crates/v/panic-rtt-target.svg)](https://crates.io/crates/panic-rtt-target) [![documentation](https://docs.rs/panic-rtt-target/badge.svg)](https://docs.rs/panic-rtt-target) 4 | 5 | Logs panic messages over RTT. A companion crate for rtt-target. 6 | 7 | ## [Documentation](https://docs.rs/panic-rtt-target) 8 | 9 | RTT must have been initialized by using one of the `rtt_init` macros. Otherwise you will get a linker error at compile time. 10 | 11 | Panics are always logged to the print channel. Upon panicking the channel mode is also automatically set to `BlockIfFull`, so that the full message will always be logged. If the code somehow manages to panic at runtime before RTT is initialized (quite unlikely), or if the print channel doesn't exist, nothing is logged. 12 | 13 | The panic handler runs in a non-returning [critical_section](https://docs.rs/critical-section) which implementation should be provided by the user. 14 | 15 | # Usage 16 | 17 | Cargo.toml: 18 | 19 | ```toml 20 | [dependencies] 21 | rtt-target = "x.y.z" 22 | panic-rtt-target = "x.y.z" 23 | ``` 24 | 25 | main.rs: 26 | 27 | ```rust 28 | #![no_std] 29 | 30 | use panic_rtt_target as _; 31 | use rtt_target::rtt_init_default; 32 | 33 | fn main() -> ! { 34 | // you can use `rtt_init_print` or you can call `set_print_channel` after initialization. 35 | rtt_init_default!(); 36 | 37 | panic!("Something has gone terribly wrong"); 38 | } 39 | ``` 40 | 41 | # Defmt support 42 | 43 | You can enable the `defmt` feature so that panics are printed to the defmt channel. If you do this 44 | and have configured both a print and a defmt channel, the panic message will be printed to both. 45 | The `defmt` feature doesn't automatically enable `rtt-target/defmt`. This allows you to use a 46 | different defmt backend if needed. 47 | -------------------------------------------------------------------------------- /panic-rtt-target/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Logs panic messages over RTT. A companion crate for rtt-target. 2 | //! 3 | //! RTT must have been initialized by using one of the `rtt_init` macros. Otherwise you will get a 4 | //! linker error at compile time. 5 | //! 6 | //! Panics are always logged to the print and defmt channels, if they are configured. Upon panicking 7 | //! the channel mode is also automatically set to `BlockIfFull`, so that the full message will 8 | //! always be logged. 9 | //! If the code somehow manages to panic at runtime before RTT is initialized (quite unlikely), 10 | //! or if the print channel doesn't exist, nothing is logged. 11 | //! 12 | //! The panic handler runs in a non-returning [critical_section](https://docs.rs/critical-section) 13 | //! which implementation should be provided by the user. 14 | //! 15 | //! # Usage 16 | //! 17 | //! Cargo.toml: 18 | //! 19 | //! ```toml 20 | //! [dependencies] 21 | //! rtt-target = "x.y.z" 22 | //! panic-rtt-target = "x.y.z" 23 | //! ``` 24 | //! 25 | //! main.rs: 26 | //! 27 | //! ```no_run 28 | //! #![no_std] 29 | //! 30 | //! use panic_rtt_target as _; 31 | //! use rtt_target::rtt_init_default; 32 | //! 33 | //! fn main() -> ! { 34 | //! // you can use `rtt_init_print`, `rtt_init_defmt` or you can call `set_print_channel` after initialization. 35 | //! rtt_init_default!(); 36 | //! 37 | //! panic!("Something has gone terribly wrong"); 38 | //! } 39 | //! ``` 40 | 41 | #![no_std] 42 | 43 | use core::{fmt::Write, panic::PanicInfo}; 44 | use portable_atomic::{compiler_fence, Ordering}; 45 | 46 | use rtt_target::{with_terminal_channel, ChannelMode}; 47 | 48 | #[inline(never)] 49 | #[panic_handler] 50 | fn panic(info: &PanicInfo) -> ! { 51 | critical_section::with(|_| { 52 | #[cfg(feature = "defmt")] 53 | defmt::error!("{}", defmt::Display2Format(info)); 54 | 55 | with_terminal_channel(|term| { 56 | term.set_mode(ChannelMode::BlockIfFull); 57 | let mut channel = term.write(0); 58 | 59 | writeln!(channel, "{}", info).ok(); 60 | }); 61 | 62 | // we should never leave critical section 63 | loop { 64 | compiler_fence(Ordering::SeqCst); 65 | } 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /panic-test/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7m-none-eabi" 3 | 4 | [unstable] 5 | build-std = ["core"] 6 | -------------------------------------------------------------------------------- /panic-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "panic-test" 3 | version = "0.1.0" 4 | authors = ["Matti Virkkunen "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | cortex-m-rt = "0.7" 9 | panic-rtt-target = { path = "../panic-rtt-target" } 10 | rtt-target = { path = "../rtt-target" } 11 | -------------------------------------------------------------------------------- /panic-test/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, error::Error, fs::File, io::Write, path::PathBuf}; 2 | 3 | fn main() -> Result<(), Box> { 4 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 5 | 6 | println!("cargo:rustc-link-search={}", out_dir.display()); 7 | File::create(out_dir.join("memory.x"))?.write_all(include_bytes!("memory.x"))?; 8 | 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /panic-test/memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for building examples for STM32F103C8xx */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 64K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 20K 6 | } 7 | -------------------------------------------------------------------------------- /panic-test/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_rtt_target as _; 5 | use rtt_target::rtt_init_default; 6 | 7 | #[cortex_m_rt::entry] 8 | fn main() -> ! { 9 | // you can use any init macro as long as it creates channel 0 10 | rtt_init_default!(); 11 | 12 | panic!("Something has gone terribly wrong"); 13 | } 14 | -------------------------------------------------------------------------------- /rtt-target/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtt-target" 3 | description = "Target side implementation of the RTT (Real-Time Transfer) I/O protocol" 4 | version = "0.6.1" 5 | edition = "2018" 6 | readme = "../README.md" 7 | keywords = ["no-std", "embedded", "debugging", "rtt"] 8 | license = "MIT" 9 | authors = ["Matti Virkkunen "] 10 | repository = "https://github.com/probe-rs/rtt-target" 11 | 12 | [features] 13 | default = [] 14 | log = ["dep:log", "dep:once_cell"] 15 | log_racy_init = [] # use log::set_logger_racy instead of log::set_logger 16 | 17 | [dependencies] 18 | ufmt-write = "0.1.0" 19 | critical-section = "1.0.0" 20 | portable-atomic = { version = "1.6.0", default-features = false } 21 | 22 | defmt = { version = "0.3.0", optional = true } 23 | log = {version = "0.4.22", optional = true} 24 | once_cell = { version = "1.20.2" , features = ["critical-section"], default-features = false, optional = true} 25 | 26 | [package.metadata.docs.rs] 27 | all-features = true 28 | rustdoc-args = ["--cfg", "docsrs"] 29 | -------------------------------------------------------------------------------- /rtt-target/src/debug.rs: -------------------------------------------------------------------------------- 1 | //! This module contains macros that work exactly like their equivalents without `debug_*` 2 | 3 | /* From init.rs */ 4 | 5 | /// The same as [`rtt_init`](crate::rtt_init) macro but works only in debug 6 | #[macro_export] 7 | macro_rules! debug_rtt_init { 8 | ($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::rtt_init!($($arg)*); }) 9 | } 10 | 11 | /// The same as [`rtt_init_default`](crate::rtt_init_default) macro but works only in debug 12 | #[macro_export] 13 | macro_rules! debug_rtt_init_default { 14 | ($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::rtt_init_default!($($arg)*); }) 15 | } 16 | 17 | /* From print.rs */ 18 | 19 | /// The same as [`rtt_init_print`](crate::rtt_init_print) macro but works only in debug 20 | #[macro_export] 21 | macro_rules! debug_rtt_init_print { 22 | ($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::rtt_init_print!($($arg)*); }) 23 | } 24 | 25 | /// The same as [`rprintln`](crate::rprintln) macro but works only in debug 26 | #[macro_export] 27 | macro_rules! debug_rprintln { 28 | ($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::rprintln!($($arg)*); }) 29 | } 30 | 31 | /// The same as [`rprint`](crate::rprint) macro but works only in debug 32 | #[macro_export] 33 | macro_rules! debug_rprint { 34 | ($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::rprint!($($arg)*); }) 35 | } 36 | -------------------------------------------------------------------------------- /rtt-target/src/defmt.rs: -------------------------------------------------------------------------------- 1 | use crate::UpChannel; 2 | use portable_atomic::{AtomicBool, Ordering}; 3 | 4 | static mut CHANNEL: Option = None; 5 | 6 | #[defmt::global_logger] 7 | struct Logger; 8 | 9 | /// Sets the channel to use for [`defmt`] macros. 10 | pub fn set_defmt_channel(channel: UpChannel) { 11 | unsafe { CHANNEL = Some(channel) } 12 | } 13 | 14 | /// Global logger lock. 15 | static TAKEN: AtomicBool = AtomicBool::new(false); 16 | static mut CS_RESTORE: critical_section::RestoreState = critical_section::RestoreState::invalid(); 17 | static mut ENCODER: defmt::Encoder = defmt::Encoder::new(); 18 | 19 | unsafe impl defmt::Logger for Logger { 20 | fn acquire() { 21 | // safety: Must be paired with corresponding call to release(), see below 22 | let restore = unsafe { critical_section::acquire() }; 23 | 24 | if TAKEN.load(Ordering::Relaxed) { 25 | panic!("defmt logger taken reentrantly") 26 | } 27 | 28 | // no need for CAS because interrupts are disabled 29 | TAKEN.store(true, Ordering::Relaxed); 30 | 31 | // safety: accessing the `static mut` is OK because we have acquired a critical section. 32 | unsafe { CS_RESTORE = restore }; 33 | 34 | // safety: accessing the `static mut` is OK because we have disabled interrupts. 35 | unsafe { 36 | let encoder = &mut *core::ptr::addr_of_mut!(ENCODER); 37 | encoder.start_frame(do_write) 38 | } 39 | } 40 | 41 | unsafe fn flush() {} 42 | 43 | unsafe fn release() { 44 | // safety: accessing the `static mut` is OK because we have acquired a critical section. 45 | let encoder = &mut *core::ptr::addr_of_mut!(ENCODER); 46 | encoder.end_frame(do_write); 47 | 48 | // safety: accessing the `static mut` is OK because we have acquired a critical section. 49 | TAKEN.store(false, Ordering::Relaxed); 50 | 51 | // safety: accessing the `static mut` is OK because we have acquired a critical section. 52 | let restore = CS_RESTORE; 53 | 54 | // safety: Must be paired with corresponding call to acquire(), see above 55 | critical_section::release(restore); 56 | } 57 | 58 | unsafe fn write(bytes: &[u8]) { 59 | // safety: accessing the `static mut` is OK because we have disabled interrupts. 60 | let encoder = &mut *core::ptr::addr_of_mut!(ENCODER); 61 | encoder.write(bytes, do_write); 62 | } 63 | } 64 | 65 | fn do_write(bytes: &[u8]) { 66 | unsafe { 67 | let channel = core::ptr::addr_of_mut!(CHANNEL); 68 | if let Some(Some(c)) = channel.as_mut() { 69 | c.write(bytes); 70 | } 71 | } 72 | } 73 | 74 | /// Initializes RTT with a single up channel and sets it as the defmt channel for the printing 75 | /// macros. 76 | /// 77 | /// The optional arguments specify the blocking mode (default: `NoBlockSkip`) and size of the buffer 78 | /// in bytes (default: 1024). See [`rtt_init`] for more details. 79 | /// 80 | /// [`rtt_init`]: crate::rtt_init 81 | #[macro_export] 82 | macro_rules! rtt_init_defmt { 83 | ($mode:path, $size:expr) => {{ 84 | let channels = $crate::rtt_init! { 85 | up: { 86 | 0: { 87 | size: $size, 88 | mode: $mode, 89 | name: "defmt" 90 | } 91 | } 92 | }; 93 | 94 | $crate::set_defmt_channel(channels.up.0); 95 | }}; 96 | 97 | ($mode:path) => { 98 | $crate::rtt_init_defmt!($mode, 1024); 99 | }; 100 | 101 | () => {{ 102 | use $crate::ChannelMode::NoBlockSkip; 103 | $crate::rtt_init_defmt!(NoBlockSkip, 1024); 104 | }}; 105 | } 106 | -------------------------------------------------------------------------------- /rtt-target/src/init.rs: -------------------------------------------------------------------------------- 1 | /// rtt_init! implementation detail 2 | #[macro_export] 3 | #[doc(hidden)] 4 | macro_rules! rtt_init_repeat { 5 | ({ $($code:tt)+ } { $($acc:tt)* }; $n:literal: { $($_:tt)* } $($tail:tt)*) => { 6 | $crate::rtt_init_repeat!({ $($code)* } { $($code)* $($acc)* }; $($tail)*) 7 | }; 8 | ({ $($code:tt)+ } { $($acc:tt)* };) => { 9 | ($($acc)*) 10 | }; 11 | } 12 | 13 | /// rtt_init! implementation detail 14 | #[macro_export] 15 | #[doc(hidden)] 16 | macro_rules! rtt_init_channels { 17 | ( 18 | $field:expr; 19 | $number:literal: { 20 | size: $size:expr 21 | $(, mode: $mode:path )? 22 | $(, name: $name:literal )? 23 | $(, section: $section:literal )? 24 | $(,)? 25 | } 26 | $($tail:tt)* 27 | ) => { 28 | let mut name: *const u8 = core::ptr::null(); 29 | $( name = concat!($name, "\0").as_bytes().as_ptr(); )? 30 | 31 | let mut mode = $crate::ChannelMode::NoBlockSkip; 32 | $( mode = $mode; )? 33 | 34 | $field[$number].init(name, mode, { 35 | $( #[link_section = $section] )? 36 | static mut _RTT_CHANNEL_BUFFER: MaybeUninit<[u8; $size]> = MaybeUninit::uninit(); 37 | _RTT_CHANNEL_BUFFER.as_mut_ptr() 38 | }); 39 | 40 | $crate::rtt_init_channels!($field; $($tail)*); 41 | }; 42 | ($field:expr;) => { }; 43 | } 44 | 45 | /// rtt_init! implementation detail 46 | #[macro_export] 47 | #[doc(hidden)] 48 | macro_rules! rtt_init_wrappers { 49 | ($field:expr; $cons:path; { $($acc:tt)* }; $n:literal: { $($_:tt)* } $($tail:tt)*) => { 50 | $crate::rtt_init_wrappers!( 51 | $field; 52 | $cons; 53 | { 54 | $($acc)* 55 | $cons(&mut $field[$n] as *mut _), 56 | }; 57 | $($tail)*) 58 | }; 59 | ($field:expr; $cons:path; { $($acc:tt)* };) => { 60 | ($($acc)*) 61 | }; 62 | } 63 | 64 | /// Initializes RTT with the specified channels. Channel numbers, buffer sizes and names can be 65 | /// defined. 66 | /// 67 | /// The syntax looks as follows (note that commas are not allowed anywhere): 68 | /// 69 | /// ``` 70 | /// let channels = rtt_init! { 71 | /// up: { 72 | /// 0: { // channel number 73 | /// size: 1024, // buffer size in bytes 74 | /// mode: NoBlockSkip, // mode (optional, default: NoBlockSkip, see enum ChannelMode) 75 | /// name: "Terminal" // name (optional, default: no name) 76 | /// section: ".segger_term_buf" // Buffer linker section (optional, default: no section) 77 | /// } 78 | /// 1: { 79 | /// size: 32 80 | /// } 81 | /// } 82 | /// down: { 83 | /// 0: { 84 | /// size: 16, 85 | /// name: "Terminal" 86 | /// } 87 | /// } 88 | /// section_cb: ".segger_rtt" // Control block linker section (optional, default: no section) 89 | /// }; 90 | /// ``` 91 | /// 92 | /// The channel numbers must start from 0 and not skip any numbers, or otherwise odd things will 93 | /// happen. The order of the channel parameters is fixed, but optional parameters can be left out. 94 | /// This macro should be called once within a function, preferably close to the start of your entry 95 | /// point. The macro must only be called once - if it's called twice in the same program a duplicate 96 | /// symbol error will occur. 97 | /// 98 | /// At compile time the macro will statically reserve space for the RTT control block and the 99 | /// channel buffers. At runtime the macro fills in the structures and prepares them for use. 100 | /// 101 | /// The macro returns a generate struct that contains the channels. The struct for the example above 102 | /// would look as follows: 103 | /// 104 | /// ``` 105 | /// struct Channels { 106 | /// up: (UpChannel, UpChannel), 107 | /// down: (DownChannel,), 108 | /// } 109 | /// ``` 110 | /// 111 | /// The channels can either be accessed by reference or moved out as needed. For example: 112 | /// 113 | /// ``` 114 | /// use core::fmt::Write; 115 | /// 116 | /// let channels = rtt_init! { ... }; 117 | /// let mut output = channels.up.0; 118 | /// writeln!(output, "Hello, world!").ok(); 119 | /// ``` 120 | #[macro_export] 121 | macro_rules! rtt_init { 122 | { 123 | $(up: { $($up:tt)* } )? 124 | $(down: { $($down:tt)* } )? 125 | $(section_cb: $section_cb:literal )? 126 | } => {{ 127 | use core::mem::MaybeUninit; 128 | use core::ptr; 129 | use core::cell::Cell; 130 | use $crate::UpChannel; 131 | use $crate::DownChannel; 132 | use $crate::rtt::*; 133 | 134 | #[repr(C)] 135 | pub struct RttControlBlock { 136 | header: RttHeader, 137 | up_channels: [RttChannel; $crate::rtt_init_repeat!({ 1 + } { 0 }; $($($up)*)?)], 138 | down_channels: [RttChannel; $crate::rtt_init_repeat!({ 1 + } { 0 }; $($($down)*)?)], 139 | } 140 | 141 | #[used] 142 | #[no_mangle] 143 | #[export_name = "_SEGGER_RTT"] 144 | $( #[link_section = $section_cb] )? 145 | pub static mut CONTROL_BLOCK: MaybeUninit = MaybeUninit::uninit(); 146 | 147 | #[allow(unused)] 148 | #[export_name = "rtt_init_must_not_be_called_multiple_times"] 149 | fn rtt_init_must_not_be_called_multiple_times() { } 150 | 151 | use ::rtt_target::export::critical_section; 152 | 153 | static INITIALIZED: critical_section::Mutex> = critical_section::Mutex::new(Cell::new(false)); 154 | critical_section::with(|cs| { 155 | if INITIALIZED.borrow(cs).get() { 156 | panic!("rtt_init! must not be called multiple times"); 157 | } 158 | INITIALIZED.borrow(cs).set(true); 159 | }); 160 | 161 | unsafe { 162 | ptr::write_bytes(CONTROL_BLOCK.as_mut_ptr(), 0, 1); 163 | 164 | let cb = &mut *CONTROL_BLOCK.as_mut_ptr(); 165 | 166 | $( $crate::rtt_init_channels!(cb.up_channels; $($up)*); )? 167 | $( $crate::rtt_init_channels!(cb.down_channels; $($down)*); )? 168 | 169 | // The header is initialized last to make it less likely an unfinished control block is 170 | // detected by the host. 171 | 172 | cb.header.init(cb.up_channels.len(), cb.down_channels.len()); 173 | 174 | pub struct Channels { 175 | $( pub up: $crate::rtt_init_repeat!({ UpChannel, } {}; $($up)*), )? 176 | $( pub down: $crate::rtt_init_repeat!({ DownChannel, } {}; $($down)*), )? 177 | } 178 | 179 | Channels { 180 | $( up: $crate::rtt_init_wrappers!(cb.up_channels; UpChannel::new; {}; $($up)*), )? 181 | $( down: $crate::rtt_init_wrappers!(cb.down_channels; DownChannel::new; {}; $($down)*), )? 182 | } 183 | } 184 | }}; 185 | } 186 | 187 | /// Initializes RTT with default up/down channels. 188 | /// 189 | /// The default channels are up channel 0 with a 1024 byte buffer and down channel 0 with a 16 byte 190 | /// buffer. Both channels are called "Terminal". This macro is equivalent to: 191 | /// 192 | /// ``` 193 | /// rtt_init! { 194 | /// up: { 195 | /// 0: { 196 | /// size: 1024, 197 | /// name: "Terminal" 198 | /// } 199 | /// } 200 | /// down: { 201 | /// 0: { 202 | /// size: 16, 203 | /// name: "Terminal" 204 | /// } 205 | /// } 206 | /// }; 207 | /// ``` 208 | /// 209 | /// See [`rtt_init`] for more details. 210 | #[macro_export] 211 | macro_rules! rtt_init_default { 212 | () => { 213 | $crate::rtt_init! { 214 | up: { 215 | 0: { 216 | size: 1024, 217 | name: "Terminal" 218 | } 219 | } 220 | down: { 221 | 0: { 222 | size: 16, 223 | name: "Terminal" 224 | } 225 | } 226 | }; 227 | }; 228 | } 229 | -------------------------------------------------------------------------------- /rtt-target/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Target side implementation of the RTT (Real-Time Transfer) I/O protocol 2 | //! 3 | //! RTT implements input and output to/from a debug probe using in-memory ring buffers and memory 4 | //! polling. This enables debug logging from the microcontroller with minimal delays and no 5 | //! blocking, making it usable even in real-time applications where e.g. semihosting delays cannot 6 | //! be tolerated. 7 | //! 8 | //! # Hardware support 9 | //! 10 | //! This crate is platform agnostic and can be used on any chip that supports background memory 11 | //! access via its debug interface. The printing macros require a critical section which is 12 | //! platform-dependent. 13 | //! 14 | //! To interface with RTT from the host computer, a debug probe such as an ST-Link or J-Link is 15 | //! required. The normal debug protocol (e.g. SWD) is used to access RTT, so no extra connections 16 | //! such as SWO pins are needed. 17 | //! 18 | //! # Initialization 19 | //! 20 | //! RTT must be initialized at the start of your program using one of the init macros. See the 21 | //! macros for more details. 22 | //! 23 | //! The initialization macros return channel objects that can be used for writing and reading. 24 | //! Different channel objects can safely be used concurrently in different contexts without locking. 25 | //! In an interrupt-based application with realtime constraints you could use a separate channel for 26 | //! every interrupt context to allow for lock-free logging. 27 | //! 28 | //! # Channels and virtual terminals 29 | //! 30 | //! RTT supports multiple *channels* in both directions. Up channels go from target to host, and 31 | //! down channels go from host to target. Each channel is identified by its direction and number. 32 | //! 33 | //! By convention channel 0 is reserved for terminal use. In the up direction there is a set of 34 | //! escape sequences that further enable the single channel to be treated as up to 16 *virtual 35 | //! terminals*. This can be used to separate different types of messages (for example, log levels) 36 | //! from each other without having to allocate memory for multiple buffers. As a downside, multiple 37 | //! threads cannot write to the same channel at once, even if using different virtual terminal 38 | //! numbers, so access has to be synchronized. Down channel 0 is conventionally used for keyboard 39 | //! input. 40 | //! 41 | //! **Note:** Some host side programs only display channel 0 by default, so to see the other 42 | //! channels you might need to configure them appropriately. 43 | //! 44 | //! The other channels can be used to either enable concurrent use from multiple sources without 45 | //! locking, or to send e.g. binary data in either direction. 46 | //! 47 | //! Channel 0 can also be used for arbitrary data, but most tools expect it to be plain text. 48 | //! 49 | //! # Channel modes 50 | //! 51 | //! By default, channels start in [`NoBlockSkip`](ChannelMode::NoBlockSkip) mode, which discards 52 | //! data if the buffer is full. This enables RTT to not crash the application if there is no debug 53 | //! probe attached or if the host is not reading the buffers. However if the application outputs 54 | //! faster than the host can read (which is easy to do, because writing is very fast), messages will 55 | //! be lost. Channels can be set to blocking mode if this is desirable, however in that case the 56 | //! application will likely freeze when the buffer fills up if a debugger is not attached. 57 | //! 58 | //! The channel mode can also be changed on the fly by the debug probe. Therefore it may be 59 | //! advantageous to use a non-blocking mode in your microcontroller code, and set a blocking mode as 60 | //! needed when debugging. That way you will never end up with an application that freezes without a 61 | //! debugger connected. 62 | //! 63 | //! # Defmt integration 64 | //! 65 | //! The `defmt` crate can be used to format messages in a way that is more efficient and more 66 | //! informative than the standard `format!` macros. To use `defmt` with RTT, the `defmt` feature 67 | //! must be enabled, and the channel must be set up with [`set_defmt_channel`] or you can use the 68 | //! [`rtt_init_defmt`] macro to initialize rtt and defmt at once. 69 | //! 70 | //! ```toml 71 | //! [dependencies] 72 | //! defmt = { version = "0.3" } 73 | //! rtt-target = { version = "0.6", features = ["defmt"] } 74 | //! ``` 75 | //! 76 | //! # Log integration 77 | //! 78 | //! Rtt-target also supports integration with the `log` crate. The `log` feature must be enabled to 79 | //! configure a logger that forwards log messages to RTT. 80 | //! The logger can be initialized with `rtt_init_log!`. 81 | //! 82 | //! ``` 83 | //! use rtt_target::rtt_init_log; 84 | //! 85 | //! fn main() -> ! { 86 | //! rtt_init_log!(); // Pass a log::LevelFilter as an argument to set the min log level different from Trace 87 | //! loop { 88 | //! log::info!("Hello, world!"); 89 | //! } 90 | //! } 91 | //! ``` 92 | //! 93 | //! # Plain Printing 94 | //! 95 | //! For no-hassle output the [`rprint`] and [`rprintln`] macros are provided. They use a single down 96 | //! channel defined at initialization time, and a critical section for synchronization, and they 97 | //! therefore work exactly like the standard `println` style macros. They can be used from any 98 | //! context. The [`rtt_init_print`] convenience macro initializes printing on channel 0. 99 | //! 100 | //! ``` 101 | //! use rtt_target::{rtt_init_print, rprintln}; 102 | //! 103 | //! fn main() -> ! { 104 | //! rtt_init_print!(); 105 | //! loop { 106 | //! rprintln!("Hello, world!"); 107 | //! } 108 | //! } 109 | //! ``` 110 | //! 111 | //! To use rtt functionality only in debug builds use macros prefixed with `debug_*`. They have 112 | //! exactly the same functionality as without debug - the only difference is that they are removed 113 | //! when built with `--release`. It's safe to use [`debug_rprintln`] and [`debug_rprint`] even if 114 | //! rtt was initialized with [`rtt_init`] instead of [`debug_rtt_init`]. 115 | //! 116 | //! Under the hood this uses the [debug-assertions] flag. Set this flag to true to include all debug 117 | //! macros also in release mode. 118 | //! 119 | //! [debug-assertions]: https://doc.rust-lang.org/cargo/reference/profiles.html#debug-assertions 120 | //! 121 | //! ``` 122 | //! use rtt_target::{debug_rtt_init_print, debug_rprintln}; 123 | //! 124 | //! fn main() -> ! { 125 | //! debug_rtt_init_print!(); // nop in --release 126 | //! loop { 127 | //! debug_rprintln!("Hello, world!"); // not present in --release 128 | //! } 129 | //! } 130 | //! ``` 131 | //! 132 | //! The macros also support an extended syntax to print to different RTT virtual terminals. 133 | //! 134 | //! Please note that because a critical section is used, printing into a blocking channel will cause 135 | //! the application to block and freeze when the buffer is full. 136 | //! 137 | //! # Reading 138 | //! 139 | //! The following example shows how to set up the RTT to read simple input sent from the host 140 | //! to the target. 141 | //! 142 | //! ``` 143 | //! use rtt_target::{rtt_init_default, rprintln}; 144 | //! 145 | //! fn main() -> ! { 146 | //! let mode = loop { 147 | //! read = channels.down.0.read(&mut read_buf); 148 | //! for i in 0..read { 149 | //! match read_buf[i] as char { 150 | //! '0' => break 0, 151 | //! '1' => break 1, 152 | //! _ => {} 153 | //! } 154 | //! } 155 | //! }; 156 | //! } 157 | //! ``` 158 | 159 | #![no_std] 160 | #![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))] 161 | 162 | use core::convert::Infallible; 163 | use core::fmt; 164 | use ufmt_write::uWrite; 165 | 166 | #[doc(hidden)] 167 | /// Public due to access from macro 168 | pub mod debug; 169 | #[cfg(feature = "defmt")] 170 | mod defmt; 171 | #[cfg(feature = "log")] 172 | mod log; 173 | /// Public due to access from macro 174 | #[doc(hidden)] 175 | pub mod rtt; 176 | 177 | mod init; 178 | mod print; 179 | 180 | pub use print::*; 181 | 182 | #[cfg(feature = "defmt")] 183 | pub use defmt::set_defmt_channel; 184 | 185 | #[cfg(feature = "log")] 186 | pub use log::*; 187 | 188 | /// RTT up (target to host) channel 189 | /// 190 | /// Supports writing binary data directly, or writing strings via [`core::fmt`] macros such as 191 | /// [`write`] as well as the ufmt crate's `uwrite` macros (use the `u` method). 192 | /// 193 | /// Note that the formatted writing implementations diverge slightly from the trait definitions in 194 | /// that if the channel is in non-blocking mode, writing will *not* block. 195 | pub struct UpChannel(*mut rtt::RttChannel); 196 | 197 | unsafe impl Send for UpChannel {} 198 | 199 | impl UpChannel { 200 | /// Public due to access from macro. 201 | #[doc(hidden)] 202 | pub unsafe fn new(channel: *mut rtt::RttChannel) -> Self { 203 | UpChannel(channel) 204 | } 205 | 206 | #[allow(clippy::mut_from_ref)] 207 | fn channel(&self) -> &mut rtt::RttChannel { 208 | unsafe { &mut *self.0 } 209 | } 210 | 211 | /// Writes `buf` to the channel and returns the number of bytes written. Behavior when the 212 | /// buffer is full is subject to the channel blocking mode. 213 | pub fn write(&mut self, buf: &[u8]) -> usize { 214 | let mut writer = self.channel().writer(); 215 | writer.write(buf); 216 | writer.commit() 217 | } 218 | 219 | /// Creates a writer for formatted writing with ufmt. 220 | /// 221 | /// The correct way to use this method is to call it once for each write operation. This is so 222 | /// that non blocking modes will work correctly. 223 | /// 224 | /// ``` 225 | /// let mut output = channels.up.0; 226 | /// uwriteln!(output.u(), "Hello, ufmt!"); 227 | /// ``` 228 | pub fn u(&mut self) -> uWriter { 229 | uWriter(self.channel().writer()) 230 | } 231 | 232 | /// Gets the current blocking mode of the channel. The default is `NoBlockSkip`. 233 | pub fn mode(&self) -> ChannelMode { 234 | self.channel().mode() 235 | } 236 | 237 | /// Sets the blocking mode of the channel 238 | pub fn set_mode(&mut self, mode: ChannelMode) { 239 | self.channel().set_mode(mode) 240 | } 241 | 242 | /// Converts the channel into a virtual terminal that can be used for writing into multiple 243 | /// virtual terminals. 244 | pub fn into_terminal(self) -> TerminalChannel { 245 | TerminalChannel::new(self) 246 | } 247 | 248 | /// Magically creates a channel out of thin air. Return `None` if the channel number is too 249 | /// high, or if the channel has not been initialized. 250 | /// 251 | /// Calling this function will cause a linking error if `rtt_init` has not been called. 252 | /// 253 | /// # Safety 254 | /// 255 | /// This function must only be called after `rtt_init` has been called. 256 | /// 257 | /// It's undefined behavior for something else to access the channel through anything else 258 | /// besides the returned object during or after calling this function. Essentially this function 259 | /// is only safe to use in panic handlers and the like that permanently disable interrupts. 260 | pub unsafe fn conjure(number: usize) -> Option { 261 | extern "C" { 262 | #[link_name = "_SEGGER_RTT"] 263 | static mut CONTROL_BLOCK: rtt::RttHeader; 264 | } 265 | 266 | let control_block = core::ptr::addr_of_mut!(CONTROL_BLOCK); 267 | if number >= (*control_block).max_up_channels() { 268 | return None; 269 | } 270 | 271 | let channels = control_block.add(1).cast::(); 272 | 273 | // First addition moves to the start of the up channel array, second addition moves to the 274 | // correct channel. 275 | let ptr = channels.add(number); 276 | 277 | if !(*ptr).is_initialized() { 278 | return None; 279 | } 280 | 281 | Some(UpChannel(ptr)) 282 | } 283 | 284 | /// Returns true if the channel is empty. 285 | pub fn is_empty(&self) -> bool { 286 | let (write, read) = self.channel().read_pointers(); 287 | write == read 288 | } 289 | 290 | /// Wait until all data has been read by the debugger. 291 | /// 292 | /// *Note: This means that if no debugger is connected or if it isn't reading the rtt data,* 293 | /// *this function will wait indefinitely.* 294 | pub fn flush(&self) { 295 | loop { 296 | if self.is_empty() { 297 | break; 298 | } 299 | core::hint::spin_loop(); 300 | } 301 | } 302 | } 303 | 304 | impl fmt::Write for UpChannel { 305 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { 306 | self.channel().writer().write_str(s) 307 | } 308 | 309 | fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), fmt::Error> { 310 | self.channel().writer().write_fmt(args) 311 | } 312 | } 313 | 314 | /// Writer for ufmt. Don't store an instance of this, but rather create a new one for every write. 315 | #[allow(non_camel_case_types)] 316 | pub struct uWriter<'c>(rtt::RttWriter<'c>); 317 | 318 | impl uWrite for uWriter<'_> { 319 | type Error = Infallible; 320 | 321 | fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { 322 | self.0.write(s.as_bytes()); 323 | Ok(()) 324 | } 325 | } 326 | 327 | /// RTT down (host to target) channel 328 | pub struct DownChannel(*mut rtt::RttChannel); 329 | 330 | unsafe impl Send for DownChannel {} 331 | 332 | impl DownChannel { 333 | /// Public due to access from macro. 334 | #[doc(hidden)] 335 | pub unsafe fn new(channel: *mut rtt::RttChannel) -> Self { 336 | DownChannel(channel) 337 | } 338 | 339 | fn channel(&mut self) -> &mut rtt::RttChannel { 340 | unsafe { &mut *self.0 } 341 | } 342 | 343 | /// Reads up to `buf.len()` bytes from the channel and return the number of bytes read. This 344 | /// method never blocks. 345 | pub fn read(&mut self, buf: &mut [u8]) -> usize { 346 | self.channel().read(buf) 347 | } 348 | } 349 | 350 | /// Specifies what to do when a channel doesn't have enough buffer space for a complete write. 351 | #[derive(Eq, PartialEq)] 352 | #[repr(usize)] 353 | pub enum ChannelMode { 354 | /// Skip writing the data completely if it doesn't fit in its entirety. 355 | NoBlockSkip = 0, 356 | 357 | /// Write as much as possible of the data and ignore the rest. 358 | NoBlockTrim = 1, 359 | 360 | /// Block (spin) if the buffer is full. If within a critical section such as inside 361 | /// [`rprintln`], this will cause the application to freeze until the host reads from the 362 | /// buffer. 363 | BlockIfFull = 2, 364 | } 365 | 366 | /// An up channel that supports writing into multiple virtual terminals within the same buffer. 367 | /// 368 | /// An [`UpChannel`] can be turned into a `TerminalChannel` by using the 369 | /// [`into_terminal`](UpChannel::into_terminal()) method. 370 | /// 371 | /// Virtual terminals allow you to share one buffer for writing multiple streams. The virtual 372 | /// terminals number from 0 to 15 and are implemented with a simple "terminal switch" sequence on 373 | /// the fly, so there is no need to declare them in advance. You could, for example, use different 374 | /// terminal numbers for messages of different priorities to separate them in a viewer program. 375 | /// Printing uses a `TerminalChannel` internally. 376 | pub struct TerminalChannel { 377 | channel: UpChannel, 378 | current: u8, 379 | } 380 | 381 | impl TerminalChannel { 382 | pub(crate) fn new(channel: UpChannel) -> Self { 383 | Self { 384 | channel, 385 | current: 0, 386 | } 387 | } 388 | 389 | /// Creates a writer to write a message to the virtual terminal specified by `number`. 390 | /// 391 | /// The correct way to use this method is to call it once for each write operation. This is so 392 | /// that non blocking modes will work correctly. 393 | /// 394 | /// The writer supports formatted writing with the standard [`Write`] and [`ufmt_write::uWrite`]. 395 | /// 396 | /// [`Write`]: fmt::Write 397 | pub fn write(&mut self, number: u8) -> TerminalWriter { 398 | const TERMINAL_ID: [u8; 16] = *b"0123456789ABCDEF"; 399 | 400 | let mut writer = self.channel.channel().writer(); 401 | 402 | if number != self.current { 403 | // The terminal switch command must be sent in full so the mode cannot be NoBlockTrim 404 | let mode = self.channel.mode(); 405 | let mode = if mode == ChannelMode::NoBlockTrim { 406 | ChannelMode::NoBlockSkip 407 | } else { 408 | mode 409 | }; 410 | 411 | writer.write_with_mode(mode, &[0xff, TERMINAL_ID[(number & 0x0f) as usize]]); 412 | 413 | self.current = number; 414 | } 415 | 416 | TerminalWriter { 417 | writer, 418 | number, 419 | current: &mut self.current, 420 | } 421 | } 422 | 423 | /// Gets the current blocking mode of the channel. The default is `NoBlockSkip`. 424 | pub fn mode(&self) -> ChannelMode { 425 | self.channel.mode() 426 | } 427 | 428 | /// Sets the blocking mode of the channel 429 | pub fn set_mode(&mut self, mode: ChannelMode) { 430 | self.channel.set_mode(mode) 431 | } 432 | 433 | /// Returns true if the channel is empty. 434 | pub fn is_empty(&self) -> bool { 435 | self.channel.is_empty() 436 | } 437 | 438 | /// Wait until all data has been read by the debugger. 439 | /// 440 | /// *Note: This means that if no debugger is connected or if it isn't reading the rtt data,* 441 | /// *this function will wait indefinitely.* 442 | pub fn flush(&self) { 443 | self.channel.flush(); 444 | } 445 | } 446 | 447 | /// Formatted writing operation. Don't store an instance of this, but rather create a new one for 448 | /// every write. 449 | pub struct TerminalWriter<'c> { 450 | writer: rtt::RttWriter<'c>, 451 | number: u8, 452 | current: &'c mut u8, 453 | } 454 | 455 | impl fmt::Write for TerminalWriter<'_> { 456 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { 457 | self.writer.write(s.as_bytes()); 458 | Ok(()) 459 | } 460 | } 461 | 462 | impl uWrite for TerminalWriter<'_> { 463 | type Error = Infallible; 464 | 465 | fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { 466 | self.writer.write(s.as_bytes()); 467 | Ok(()) 468 | } 469 | } 470 | 471 | impl Drop for TerminalWriter<'_> { 472 | fn drop(&mut self) { 473 | if !self.writer.is_failed() { 474 | *self.current = self.number; 475 | } 476 | } 477 | } 478 | 479 | /// Used to reexport items for use in macros. Do not use directly. 480 | /// Not covered by semver guarantees. 481 | #[doc(hidden)] 482 | pub mod export { 483 | pub use critical_section; 484 | } 485 | -------------------------------------------------------------------------------- /rtt-target/src/log.rs: -------------------------------------------------------------------------------- 1 | use crate::rprintln; 2 | use once_cell::sync::OnceCell; 3 | 4 | struct Logger { 5 | level_filter: log::LevelFilter, 6 | } 7 | 8 | impl log::Log for Logger { 9 | /// Returns if logger is enabled. 10 | fn enabled(&self, metadata: &log::Metadata) -> bool { 11 | metadata.level() <= self.level_filter 12 | } 13 | 14 | /// Log the record. 15 | fn log(&self, record: &log::Record) { 16 | if self.enabled(record.metadata()) { 17 | rprintln!( 18 | "{:<5} [{}] {}", 19 | record.level(), 20 | record.target(), 21 | record.args() 22 | ); 23 | } 24 | } 25 | 26 | /// Flush buffered records. 27 | fn flush(&self) { 28 | // Nothing to do here 29 | } 30 | } 31 | 32 | static LOGGER: OnceCell = OnceCell::new(); 33 | 34 | /// Init the logger with maximum level (Trace). 35 | /// 36 | /// Note: Normally there is no need to call this manually, use `rtt_init_log!` instead. 37 | pub fn init_logger() { 38 | init_logger_with_level(log::LevelFilter::Trace); 39 | } 40 | 41 | /// Init the logger with a specific level. 42 | /// 43 | /// Note: Normally there is no need to call this manually, use `rtt_init_log!` instead. 44 | pub fn init_logger_with_level(level_filter: log::LevelFilter) { 45 | // Logger was already initialized. 46 | if LOGGER.get().is_some() { 47 | return; 48 | } 49 | let logger = LOGGER.get_or_init(|| Logger { level_filter }); 50 | 51 | // Use racy init if the feature is enabled or the target doesn't support atomic pointers. 52 | #[cfg(any(not(target_has_atomic = "ptr"), feature = "log_racy_init"))] 53 | unsafe { 54 | init_racy(logger); 55 | } 56 | 57 | // Use the default init otherwise. 58 | #[cfg(all(target_has_atomic = "ptr", not(feature = "log_racy_init")))] 59 | init_default(logger); 60 | } 61 | 62 | #[cfg(all(target_has_atomic = "ptr", not(feature = "log_racy_init")))] 63 | fn init_default(logger: &'static Logger) { 64 | log::set_logger(logger).ok(); 65 | log::set_max_level(logger.level_filter); 66 | } 67 | 68 | // # Safety 69 | // 70 | // This function will call the unsafe functions [log::set_logger_racy] and 71 | // [log::set_max_level_racy] if either the feature `log_racy_init` is enabled or the target doesn't 72 | // support atomic pointers. The [once_cell::OnceCell] should ensure that this is only called 73 | // once. 74 | #[cfg(any(not(target_has_atomic = "ptr"), feature = "log_racy_init"))] 75 | unsafe fn init_racy(logger: &'static Logger) { 76 | log::set_logger_racy(logger).ok(); 77 | log::set_max_level_racy(logger.level_filter); 78 | } 79 | 80 | /// Initializes RTT with a single up channel, sets it as the print channel for the printing macros 81 | /// and sets up a log backend with the given log level. 82 | /// 83 | /// The optional arguments specify the level filter (default: `log::LevelFilter::Trace`), 84 | /// the blocking mode (default: `NoBlockSkip`) and size of the buffer in bytes (default: 1024). 85 | /// 86 | /// See [`rtt_init`] for more details. 87 | /// 88 | /// [`rtt_init`]: crate::rtt_init 89 | #[macro_export] 90 | macro_rules! rtt_init_log { 91 | ($level:path, $mode:path, $size:expr) => {{ 92 | $crate::rtt_init_print!($mode, $size); 93 | $crate::init_logger_with_level($level); 94 | }}; 95 | 96 | ($level:path, $mode:path) => { 97 | $crate::rtt_init_log!($level, $mode, 1024); 98 | }; 99 | 100 | ($level:path) => {{ 101 | use $crate::ChannelMode::NoBlockSkip; 102 | $crate::rtt_init_log!($level, NoBlockSkip, 1024); 103 | }}; 104 | 105 | () => {{ 106 | use log::LevelFilter::Trace; 107 | use $crate::ChannelMode::NoBlockSkip; 108 | $crate::rtt_init_log!(Trace, NoBlockSkip, 1024); 109 | }}; 110 | } 111 | -------------------------------------------------------------------------------- /rtt-target/src/print.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | use core::fmt::{self, Write as _}; 3 | use critical_section::Mutex; 4 | 5 | use crate::{TerminalChannel, TerminalWriter, UpChannel}; 6 | 7 | static PRINT_TERMINAL: Mutex>> = Mutex::new(RefCell::new(None)); 8 | 9 | /// Sets the channel to use for [`rprint`], [`rprintln`], [`debug_rprint`] and [`debug_rprintln`]. 10 | /// 11 | /// [`rprint`]: crate::rprint 12 | /// [`rprintln`]: crate::rprintln 13 | /// [`debug_rprint`]: crate::debug_rprint 14 | /// [`debug_rprintln`]: crate::debug_rprintln 15 | pub fn set_print_channel(channel: UpChannel) { 16 | critical_section::with(|cs| { 17 | *PRINT_TERMINAL.borrow_ref_mut(cs) = Some(TerminalChannel::new(UpChannel(channel.0))) 18 | }); 19 | } 20 | 21 | /// Allows accessing the currently set print channel. 22 | pub fn with_terminal_channel(f: F) { 23 | critical_section::with(|cs| { 24 | if let Some(term) = &mut *PRINT_TERMINAL.borrow_ref_mut(cs) { 25 | f(term) 26 | } 27 | }); 28 | } 29 | 30 | /// Public due to access from macro. 31 | #[doc(hidden)] 32 | pub mod print_impl { 33 | use super::*; 34 | 35 | fn with_writer(number: u8, f: F) { 36 | with_terminal_channel(|term| f(term.write(number))); 37 | } 38 | 39 | /// Public due to access from macro. 40 | #[doc(hidden)] 41 | pub fn write_str(number: u8, s: &str) { 42 | with_writer(number, |mut w| { 43 | w.write_str(s).ok(); 44 | }); 45 | } 46 | 47 | /// Public due to access from macro. 48 | #[doc(hidden)] 49 | pub fn write_fmt(number: u8, arg: fmt::Arguments) { 50 | with_writer(number, |mut w| { 51 | w.write_fmt(arg).ok(); 52 | }); 53 | } 54 | } 55 | 56 | /// Prints to the print RTT channel. Works just like the standard `print`. 57 | /// 58 | /// Before use the print channel has to be set with [`rtt_init_print`] or [`set_print_channel`]. If 59 | /// the channel isn't set, the message is silently discarded. 60 | /// 61 | /// The macro also supports output to multiple virtual terminals on the channel. Use the syntax 62 | /// `rprint!(=> 1, "Hello!");` to write to terminal number 1, for example. Terminal numbers 63 | /// range from 0 to 15. 64 | /// 65 | /// [`rtt_init_print`]: crate::rtt_init_print 66 | #[macro_export] 67 | macro_rules! rprint { 68 | (=> $terminal:expr, $s:expr) => { 69 | $crate::print_impl::write_str($terminal, $s); 70 | }; 71 | (=> $terminal:expr, $($arg:tt)*) => { 72 | $crate::print_impl::write_fmt($terminal, format_args!($($arg)*)); 73 | }; 74 | ($s:expr) => { 75 | $crate::print_impl::write_str(0, $s); 76 | }; 77 | ($($arg:tt)*) => { 78 | $crate::print_impl::write_fmt(0, format_args!($($arg)*)); 79 | }; 80 | } 81 | 82 | /// Prints to the print RTT channel, with a newline. Works just like the standard `println`. 83 | /// 84 | /// Before use the print channel has to be set with [`rtt_init_print`] or [`set_print_channel`]. If 85 | /// the channel isn't set, the message is silently discarded. 86 | /// 87 | /// The macro also supports output to multiple virtual terminals on the channel. Use the syntax 88 | /// `rprintln!(=> 1, "Hello!");` to write to terminal number 1, for example. Terminal numbers 89 | /// range from 0 to 15. 90 | /// 91 | /// [`rtt_init_print`]: crate::rtt_init_print 92 | #[macro_export] 93 | macro_rules! rprintln { 94 | (=> $terminal:expr) => { 95 | $crate::print_impl::write_str($terminal, "\n"); 96 | }; 97 | (=> $terminal:expr, $fmt:expr) => { 98 | $crate::print_impl::write_str($terminal, concat!($fmt, "\n")); 99 | }; 100 | (=> $terminal:expr, $fmt:expr, $($arg:tt)*) => { 101 | $crate::print_impl::write_fmt($terminal, format_args!(concat!($fmt, "\n"), $($arg)*)); 102 | }; 103 | () => { 104 | $crate::print_impl::write_str(0, "\n"); 105 | }; 106 | ($fmt:expr) => { 107 | $crate::print_impl::write_str(0, concat!($fmt, "\n")); 108 | }; 109 | ($fmt:expr, $($arg:tt)*) => { 110 | $crate::print_impl::write_fmt(0, format_args!(concat!($fmt, "\n"), $($arg)*)); 111 | }; 112 | } 113 | 114 | /// Print to RTT and return the value of a given expression for quick debugging. This is equivalent 115 | /// to Rust's `std::dbg!()` macro. 116 | #[macro_export] 117 | macro_rules! rdbg { 118 | (=> $terminal:expr) => { 119 | $crate::rprintln!(=> $terminal, "[{}:{}]", ::core::file!(), ::core::line!()) 120 | }; 121 | (=> $terminal:expr, $val:expr $(,)?) => { 122 | // Use of `match` here is intentional because it affects the lifetimes 123 | // of temporaries - https://stackoverflow.com/a/48732525/1063961 124 | match $val { 125 | tmp => { 126 | $crate::rprintln!(=> $terminal, "[{}:{}] {} = {:#?}", 127 | ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp); 128 | tmp 129 | } 130 | } 131 | }; 132 | (=> $terminal:expr, $($val:expr),+ $(,)?) => { 133 | ($($crate::rdbg!(=> $terminal, $val)),+,) 134 | }; 135 | () => { 136 | $crate::rprintln!("[{}:{}]", ::core::file!(), ::core::line!()) 137 | }; 138 | ($val:expr $(,)?) => { 139 | // Use of `match` here is intentional because it affects the lifetimes 140 | // of temporaries - https://stackoverflow.com/a/48732525/1063961 141 | match $val { 142 | tmp => { 143 | $crate::rprintln!("[{}:{}] {} = {:#?}", 144 | ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp); 145 | tmp 146 | } 147 | } 148 | }; 149 | ($($val:expr),+ $(,)?) => { 150 | ($($crate::rdbg!($val)),+,) 151 | }; 152 | } 153 | 154 | /// Initializes RTT with a single up channel and sets it as the print channel for the printing 155 | /// macros. 156 | /// 157 | /// The optional arguments specify the blocking mode (default: `NoBlockSkip`) and size of the buffer 158 | /// in bytes (default: 1024). See [`rtt_init`] for more details. 159 | /// 160 | /// [`rtt_init`]: crate::rtt_init 161 | #[macro_export] 162 | macro_rules! rtt_init_print { 163 | ($mode:path, $size:expr) => {{ 164 | let channels = $crate::rtt_init! { 165 | up: { 166 | 0: { 167 | size: $size, 168 | mode: $mode, 169 | name: "Terminal" 170 | } 171 | } 172 | }; 173 | 174 | $crate::set_print_channel(channels.up.0); 175 | }}; 176 | 177 | ($mode:path) => { 178 | $crate::rtt_init_print!($mode, 1024); 179 | }; 180 | 181 | () => {{ 182 | use $crate::ChannelMode::NoBlockSkip; 183 | $crate::rtt_init_print!(NoBlockSkip, 1024); 184 | }}; 185 | } 186 | -------------------------------------------------------------------------------- /rtt-target/src/rtt.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the implementation for the RTT protocol. It's not meant to be used directly 2 | //! in user code, and therefore mostly undocumented. The module is only public so that it can be 3 | //! accessed from the rtt_init! macro. 4 | 5 | use crate::ChannelMode; 6 | use core::cmp::min; 7 | use core::fmt; 8 | use core::ptr; 9 | use portable_atomic::{AtomicUsize, Ordering::SeqCst}; 10 | 11 | // Note: this is zero-initialized in the initialization macro so all zeros must be a valid value 12 | #[repr(C)] 13 | pub struct RttHeader { 14 | id: [u8; 16], 15 | max_up_channels: usize, 16 | max_down_channels: usize, 17 | // Followed in memory by: 18 | // up_channels: [Channel; max_up_channels] 19 | // down_channels: [Channel; down_up_channels] 20 | } 21 | 22 | impl RttHeader { 23 | /// Initializes the control block header. 24 | /// 25 | /// # Safety 26 | /// 27 | /// The arguments must correspond to the sizes of the arrays that follow the header in memory. 28 | pub unsafe fn init(&mut self, max_up_channels: usize, max_down_channels: usize) { 29 | ptr::write_volatile(&mut self.max_up_channels, max_up_channels); 30 | ptr::write_volatile(&mut self.max_down_channels, max_down_channels); 31 | 32 | // Copy the ID backward to avoid storing the magic string in the binary. The ID is 33 | // written backwards to make it less likely an unfinished control block is detected by the host. 34 | 35 | const MAGIC_STR_BACKWARDS: &[u8; 16] = b"\0\0\0\0\0\0TTR REGGES"; 36 | 37 | for (idx, byte) in MAGIC_STR_BACKWARDS.into_iter().enumerate() { 38 | ptr::write_volatile(&mut self.id[15 - idx], *byte); 39 | } 40 | } 41 | 42 | pub fn max_up_channels(&self) -> usize { 43 | self.max_up_channels 44 | } 45 | } 46 | 47 | // Note: this is zero-initialized in the initialization macro so all zeros must be a valid value 48 | #[repr(C)] 49 | pub struct RttChannel { 50 | name: *const u8, 51 | buffer: *mut u8, 52 | size: usize, 53 | write: AtomicUsize, 54 | read: AtomicUsize, 55 | flags: AtomicUsize, 56 | } 57 | 58 | impl RttChannel { 59 | /// Initializes the channel. 60 | /// 61 | /// # Safety 62 | /// 63 | /// The pointer arguments must point to a valid null-terminated name and writable buffer. 64 | pub unsafe fn init(&mut self, name: *const u8, mode: ChannelMode, buffer: *mut [u8]) { 65 | ptr::write_volatile(&mut self.name, name); 66 | ptr::write_volatile(&mut self.size, (*buffer).len()); 67 | self.set_mode(mode); 68 | 69 | // Set buffer last as it can be used to detect if the channel has been initialized 70 | ptr::write_volatile(&mut self.buffer, buffer as *mut u8); 71 | } 72 | 73 | /// Returns true on a non-null value of the (raw) buffer pointer 74 | pub fn is_initialized(&self) -> bool { 75 | !self.buffer.is_null() 76 | } 77 | 78 | pub(crate) fn mode(&self) -> ChannelMode { 79 | let mode = self.flags.load(SeqCst) & 3; 80 | 81 | match mode { 82 | 0 => ChannelMode::NoBlockSkip, 83 | 1 => ChannelMode::NoBlockTrim, 84 | 2 => ChannelMode::BlockIfFull, 85 | _ => ChannelMode::NoBlockSkip, 86 | } 87 | } 88 | 89 | pub(crate) fn set_mode(&self, mode: ChannelMode) { 90 | self.flags 91 | .store((self.flags.load(SeqCst) & !3) | mode as usize, SeqCst); 92 | } 93 | 94 | // This method should only be called for down channels. 95 | pub(crate) fn read(&self, mut buf: &mut [u8]) -> usize { 96 | let (write, mut read) = self.read_pointers(); 97 | 98 | let mut total = 0; 99 | 100 | // Read while buffer contains data and output buffer has space (maximum of two iterations) 101 | while !buf.is_empty() { 102 | let count = min(self.readable_contiguous(write, read), buf.len()); 103 | if count == 0 { 104 | break; 105 | } 106 | 107 | unsafe { 108 | ptr::copy_nonoverlapping(self.buffer.add(read), buf.as_mut_ptr(), count); 109 | } 110 | 111 | total += count; 112 | read += count; 113 | 114 | if read >= self.size { 115 | // Wrap around to start 116 | read = 0; 117 | } 118 | 119 | buf = &mut buf[count..]; 120 | } 121 | 122 | self.read.store(read, SeqCst); 123 | 124 | total 125 | } 126 | 127 | /// This method should only be called for up channels. 128 | pub(crate) fn writer(&self) -> RttWriter<'_> { 129 | RttWriter { 130 | chan: self, 131 | write: self.read_pointers().0, 132 | total: 0, 133 | state: WriteState::Writable, 134 | } 135 | } 136 | 137 | /// Gets the amount of contiguous data available for reading 138 | fn readable_contiguous(&self, write: usize, read: usize) -> usize { 139 | if read > write { 140 | self.size - read 141 | } else { 142 | write - read 143 | } 144 | } 145 | 146 | pub(crate) fn read_pointers(&self) -> (usize, usize) { 147 | let write = self.write.load(SeqCst); 148 | let read = self.read.load(SeqCst); 149 | 150 | if write >= self.size || read >= self.size { 151 | // Pointers have been corrupted. This doesn't happen in well-behaved programs, so 152 | // attempt to reset the buffer. 153 | 154 | self.write.store(0, SeqCst); 155 | self.read.store(0, SeqCst); 156 | return (0, 0); 157 | } 158 | 159 | (write, read) 160 | } 161 | } 162 | 163 | /// A cancellable write operation to an RTT channel. 164 | pub(crate) struct RttWriter<'c> { 165 | chan: &'c RttChannel, 166 | write: usize, 167 | total: usize, 168 | state: WriteState, 169 | } 170 | 171 | #[derive(Eq, PartialEq)] 172 | enum WriteState { 173 | /// Operation can continue 174 | Writable, 175 | 176 | /// Buffer space ran out but the written data will still be committed 177 | Full, 178 | 179 | /// The operation failed and won't be committed, or it has already been committed. 180 | Finished, 181 | } 182 | 183 | impl RttWriter<'_> { 184 | pub fn write(&mut self, buf: &[u8]) { 185 | self.write_with_mode(self.chan.mode(), buf); 186 | } 187 | 188 | pub fn write_with_mode(&mut self, mode: ChannelMode, mut buf: &[u8]) { 189 | while self.state == WriteState::Writable && !buf.is_empty() { 190 | let count = min(self.writable_contiguous(), buf.len()); 191 | 192 | if count == 0 { 193 | // Buffer is full 194 | 195 | match mode { 196 | ChannelMode::NoBlockSkip => { 197 | // Mark the entire operation as failed if even one part cannot be written in 198 | // full. 199 | self.state = WriteState::Finished; 200 | return; 201 | } 202 | 203 | ChannelMode::NoBlockTrim => { 204 | // If the buffer is full, write as much as possible (note: no return), and 205 | // mark the operation as full, which prevents further writes. 206 | self.state = WriteState::Full; 207 | } 208 | 209 | ChannelMode::BlockIfFull => { 210 | // Commit everything written so far and spin until more can be written 211 | self.chan.write.store(self.write, SeqCst); 212 | continue; 213 | } 214 | } 215 | } 216 | 217 | unsafe { 218 | ptr::copy_nonoverlapping(buf.as_ptr(), self.chan.buffer.add(self.write), count); 219 | } 220 | 221 | self.write += count; 222 | self.total += count; 223 | 224 | if self.write >= self.chan.size { 225 | // Wrap around to start 226 | self.write = 0; 227 | } 228 | 229 | buf = &buf[count..]; 230 | } 231 | } 232 | 233 | /// Gets the amount of contiguous space available for writing 234 | fn writable_contiguous(&self) -> usize { 235 | let read = self.chan.read_pointers().1; 236 | 237 | if read > self.write { 238 | read - self.write - 1 239 | } else if read == 0 { 240 | self.chan.size - self.write - 1 241 | } else { 242 | self.chan.size - self.write 243 | } 244 | } 245 | 246 | pub fn is_failed(&self) -> bool { 247 | self.state != WriteState::Finished 248 | } 249 | 250 | pub fn commit(mut self) -> usize { 251 | self.commit_impl(); 252 | 253 | self.total 254 | } 255 | 256 | fn commit_impl(&mut self) { 257 | match self.state { 258 | WriteState::Finished => (), 259 | WriteState::Full | WriteState::Writable => { 260 | // Commit the write pointer so the host can see the new data 261 | self.chan.write.store(self.write, SeqCst); 262 | self.state = WriteState::Finished; 263 | } 264 | } 265 | } 266 | } 267 | 268 | impl Drop for RttWriter<'_> { 269 | fn drop(&mut self) { 270 | self.commit_impl(); 271 | } 272 | } 273 | 274 | impl fmt::Write for RttWriter<'_> { 275 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { 276 | self.write(s.as_bytes()); 277 | Ok(()) 278 | } 279 | } 280 | --------------------------------------------------------------------------------