Skip to content

Commit

Permalink
Merge pull request #55 from probe-rs/prep
Browse files Browse the repository at this point in the history
Last-minute changes
  • Loading branch information
Yatekii authored Nov 21, 2024
2 parents 5b7603b + 553b355 commit ab94030
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Target side implementation of the RTT (Real-Time Transfer) I/O protocol. RTT imp

## Platform support

To use the global `rprintln!` macro, a platform-specific [`critical-section`](https://github.com/rust-embedded/critical-section) implementation is needed for locking.
A platform-specific [`critical-section`](https://github.com/rust-embedded/critical-section) implementation is needed to use this library.

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.

Expand Down
13 changes: 2 additions & 11 deletions examples-cortex-m/src/bin/defmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,11 @@

use cortex_m_rt::entry;
use panic_halt as _;
use rtt_target::rtt_init;
use rtt_target::rtt_init_defmt;

#[entry]
fn main() -> ! {
let channels = rtt_init! {
up: {
0: {
size: 1024,
name: "defmt"
}
}
};

rtt_target::set_defmt_channel(channels.up.0);
rtt_init_defmt!();

let mut i = 0;
loop {
Expand Down
9 changes: 9 additions & 0 deletions panic-rtt-target/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ repository = "https://github.com/probe-rs/rtt-target"
rtt-target = {version = "0.6.0", path = "../rtt-target" }
critical-section = "1.1.1"
portable-atomic = { version = "1.6.0", default-features = false }

defmt = { version = "0.3.0", optional = true }

[features]
default = []
defmt = ["dep:defmt"]

[package.metadata.docs.rs]
features = ["defmt"]
7 changes: 7 additions & 0 deletions panic-rtt-target/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,10 @@ fn main() -> ! {
panic!("Something has gone terribly wrong");
}
```

# Defmt support

You can enable the `defmt` feature so that panics are printed to the defmt channel. If you do this
and have configured both a print and a defmt channel, the panic message will be printed to both.
The `defmt` feature doesn't automatically enable `rtt-target/defmt`. This allows you to use a
different defmt backend if needed.
10 changes: 7 additions & 3 deletions panic-rtt-target/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
//! RTT must have been initialized by using one of the `rtt_init` macros. Otherwise you will get a
//! linker error at compile time.
//!
//! 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.
//! Panics are always logged to the print and defmt channels, if they are configured. 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.
//!
Expand All @@ -30,7 +31,7 @@
//! use rtt_target::rtt_init_default;
//!
//! fn main() -> ! {
//! // you can use `rtt_init_print` or you can call `set_print_channel` after initialization.
//! // you can use `rtt_init_print`, `rtt_init_defmt` or you can call `set_print_channel` after initialization.
//! rtt_init_default!();
//!
//! panic!("Something has gone terribly wrong");
Expand All @@ -48,6 +49,9 @@ use rtt_target::{with_terminal_channel, ChannelMode};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
critical_section::with(|_| {
#[cfg(feature = "defmt")]
defmt::error!("{}", defmt::Display2Format(info));

with_terminal_channel(|term| {
term.set_mode(ChannelMode::BlockIfFull);
let mut channel = term.write(0);
Expand Down
4 changes: 4 additions & 0 deletions rtt-target/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ critical-section = "1.0.0"
portable-atomic = { version = "1.6.0", default-features = false }

defmt = { version = "0.3.0", optional = true }

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
44 changes: 41 additions & 3 deletions rtt-target/src/defmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@ unsafe impl defmt::Logger for Logger {
unsafe { CS_RESTORE = restore };

// safety: accessing the `static mut` is OK because we have disabled interrupts.
unsafe { ENCODER.start_frame(do_write) }
unsafe {
let encoder = &mut *core::ptr::addr_of_mut!(ENCODER);
encoder.start_frame(do_write)
}
}

unsafe fn flush() {}

unsafe fn release() {
// safety: accessing the `static mut` is OK because we have acquired a critical section.
ENCODER.end_frame(do_write);
let encoder = &mut *core::ptr::addr_of_mut!(ENCODER);
encoder.end_frame(do_write);

// safety: accessing the `static mut` is OK because we have acquired a critical section.
TAKEN.store(false, Ordering::Relaxed);
Expand All @@ -53,7 +57,8 @@ unsafe impl defmt::Logger for Logger {

unsafe fn write(bytes: &[u8]) {
// safety: accessing the `static mut` is OK because we have disabled interrupts.
ENCODER.write(bytes, do_write);
let encoder = &mut *core::ptr::addr_of_mut!(ENCODER);
encoder.write(bytes, do_write);
}
}

Expand All @@ -65,3 +70,36 @@ fn do_write(bytes: &[u8]) {
}
}
}

/// Initializes RTT with a single up channel and sets it as the defmt channel for the printing
/// macros.
///
/// The optional arguments specify the blocking mode (default: `NoBlockSkip`) and size of the buffer
/// in bytes (default: 1024). See [`rtt_init`] for more details.
///
/// [`rtt_init`]: crate::rtt_init
#[macro_export]
macro_rules! rtt_init_defmt {
($mode:path, $size:expr) => {
let channels = $crate::rtt_init! {
up: {
0: {
size: $size,
mode: $mode,
name: "defmt"
}
}
};

$crate::set_defmt_channel(channels.up.0);
};

($mode:path) => {
$crate::rtt_init_defmt!($mode, 1024);
};

() => {
use $crate::ChannelMode::NoBlockSkip;
$crate::rtt_init_defmt!(NoBlockSkip, 1024);
};
}
15 changes: 15 additions & 0 deletions rtt-target/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ macro_rules! rtt_init {
} => {{
use core::mem::MaybeUninit;
use core::ptr;
use core::cell::Cell;
use $crate::UpChannel;
use $crate::DownChannel;
use $crate::rtt::*;
Expand All @@ -137,6 +138,20 @@ macro_rules! rtt_init {
#[export_name = "_SEGGER_RTT"]
pub static mut CONTROL_BLOCK: MaybeUninit<RttControlBlock> = MaybeUninit::uninit();

#[allow(unused)]
#[export_name = "rtt_init_must_not_be_called_multiple_times"]
fn rtt_init_must_not_be_called_multiple_times() { }

use ::rtt_target::export::critical_section;

static INITIALIZED: critical_section::Mutex<Cell<bool>> = critical_section::Mutex::new(Cell::new(false));
critical_section::with(|cs| {
if INITIALIZED.borrow(cs).get() {
panic!("rtt_init! must not be called multiple times");
}
INITIALIZED.borrow(cs).set(true);
});

unsafe {
ptr::write_bytes(CONTROL_BLOCK.as_mut_ptr(), 0, 1);

Expand Down
20 changes: 16 additions & 4 deletions rtt-target/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,10 @@
//! ```
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))]

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

#[doc(hidden)]
Expand Down Expand Up @@ -234,22 +234,27 @@ impl UpChannel {
///
/// # Safety
///
/// This function must only be called after `rtt_init` has been called.
///
/// 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>;
static mut CONTROL_BLOCK: rtt::RttHeader;
}

if number >= (*CONTROL_BLOCK.as_ptr()).max_up_channels() {
let control_block = core::ptr::addr_of_mut!(CONTROL_BLOCK);
if number >= (*control_block).max_up_channels() {
return None;
}

let channels = control_block.add(1).cast::<rtt::RttChannel>();

// 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);
let ptr = channels.add(number);

if !(*ptr).is_initialized() {
return None;
Expand Down Expand Up @@ -452,3 +457,10 @@ impl Drop for TerminalWriter<'_> {
}
}
}

/// Used to reexport items for use in macros. Do not use directly.
/// Not covered by semver guarantees.
#[doc(hidden)]
pub mod export {
pub use critical_section;
}
18 changes: 7 additions & 11 deletions rtt-target/src/rtt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::ChannelMode;
use core::cmp::min;
use core::fmt;
use core::ptr;
use portable_atomic::{fence, AtomicUsize, Ordering::SeqCst};
use portable_atomic::{AtomicUsize, Ordering::SeqCst};

// Note: this is zero-initialized in the initialization macro so all zeros must be a valid value
#[repr(C)]
Expand All @@ -29,18 +29,14 @@ impl RttHeader {
ptr::write_volatile(&mut self.max_up_channels, max_up_channels);
ptr::write_volatile(&mut self.max_down_channels, max_down_channels);

// Copy the ID in two parts to avoid having the ID string in memory in full. The ID is
// copied last to make it less likely an unfinished control block is detected by the host.
// Copy the ID backward to avoid storing the magic string in the binary. The ID is
// written backwards to make it less likely an unfinished control block is detected by the host.

ptr::copy_nonoverlapping(b"SEGG_" as *const u8, self.id.as_mut_ptr(), 5);
const MAGIC_STR_BACKWARDS: &[u8; 16] = b"\0\0\0\0\0\0TTR REGGES";

fence(SeqCst);

ptr::copy_nonoverlapping(
b"ER RTT\0\0\0\0\0\0" as *const u8,
self.id.as_mut_ptr().offset(4),
12,
);
for (idx, byte) in MAGIC_STR_BACKWARDS.into_iter().enumerate() {
ptr::write_volatile(&mut self.id[15 - idx], *byte);
}
}

pub fn max_up_channels(&self) -> usize {
Expand Down

0 comments on commit ab94030

Please sign in to comment.