Skip to content

Commit ab94030

Browse files
authored
Merge pull request #55 from probe-rs/prep
Last-minute changes
2 parents 5b7603b + 553b355 commit ab94030

File tree

10 files changed

+109
-33
lines changed

10 files changed

+109
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Target side implementation of the RTT (Real-Time Transfer) I/O protocol. RTT imp
88

99
## Platform support
1010

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

1313
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.
1414

examples-cortex-m/src/bin/defmt.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,11 @@
33

44
use cortex_m_rt::entry;
55
use panic_halt as _;
6-
use rtt_target::rtt_init;
6+
use rtt_target::rtt_init_defmt;
77

88
#[entry]
99
fn main() -> ! {
10-
let channels = rtt_init! {
11-
up: {
12-
0: {
13-
size: 1024,
14-
name: "defmt"
15-
}
16-
}
17-
};
18-
19-
rtt_target::set_defmt_channel(channels.up.0);
10+
rtt_init_defmt!();
2011

2112
let mut i = 0;
2213
loop {

panic-rtt-target/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ repository = "https://github.com/probe-rs/rtt-target"
1616
rtt-target = {version = "0.6.0", path = "../rtt-target" }
1717
critical-section = "1.1.1"
1818
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"]

panic-rtt-target/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ fn main() -> ! {
3737
panic!("Something has gone terribly wrong");
3838
}
3939
```
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.

panic-rtt-target/src/lib.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
//! RTT must have been initialized by using one of the `rtt_init` macros. Otherwise you will get a
44
//! linker error at compile time.
55
//!
6-
//! Panics are always logged to the print channel. Upon panicking the channel mode is also
7-
//! automatically set to `BlockIfFull`, so that the full message will always be logged.
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.
89
//! If the code somehow manages to panic at runtime before RTT is initialized (quite unlikely),
910
//! or if the print channel doesn't exist, nothing is logged.
1011
//!
@@ -30,7 +31,7 @@
3031
//! use rtt_target::rtt_init_default;
3132
//!
3233
//! fn main() -> ! {
33-
//! // you can use `rtt_init_print` or you can call `set_print_channel` after initialization.
34+
//! // you can use `rtt_init_print`, `rtt_init_defmt` or you can call `set_print_channel` after initialization.
3435
//! rtt_init_default!();
3536
//!
3637
//! panic!("Something has gone terribly wrong");
@@ -48,6 +49,9 @@ use rtt_target::{with_terminal_channel, ChannelMode};
4849
#[panic_handler]
4950
fn panic(info: &PanicInfo) -> ! {
5051
critical_section::with(|_| {
52+
#[cfg(feature = "defmt")]
53+
defmt::error!("{}", defmt::Display2Format(info));
54+
5155
with_terminal_channel(|term| {
5256
term.set_mode(ChannelMode::BlockIfFull);
5357
let mut channel = term.write(0);

rtt-target/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ critical-section = "1.0.0"
1818
portable-atomic = { version = "1.6.0", default-features = false }
1919

2020
defmt = { version = "0.3.0", optional = true }
21+
22+
[package.metadata.docs.rs]
23+
all-features = true
24+
rustdoc-args = ["--cfg", "docsrs"]

rtt-target/src/defmt.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,18 @@ unsafe impl defmt::Logger for Logger {
3232
unsafe { CS_RESTORE = restore };
3333

3434
// safety: accessing the `static mut` is OK because we have disabled interrupts.
35-
unsafe { ENCODER.start_frame(do_write) }
35+
unsafe {
36+
let encoder = &mut *core::ptr::addr_of_mut!(ENCODER);
37+
encoder.start_frame(do_write)
38+
}
3639
}
3740

3841
unsafe fn flush() {}
3942

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

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

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

@@ -65,3 +70,36 @@ fn do_write(bytes: &[u8]) {
6570
}
6671
}
6772
}
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+
}

rtt-target/src/init.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ macro_rules! rtt_init {
121121
} => {{
122122
use core::mem::MaybeUninit;
123123
use core::ptr;
124+
use core::cell::Cell;
124125
use $crate::UpChannel;
125126
use $crate::DownChannel;
126127
use $crate::rtt::*;
@@ -137,6 +138,20 @@ macro_rules! rtt_init {
137138
#[export_name = "_SEGGER_RTT"]
138139
pub static mut CONTROL_BLOCK: MaybeUninit<RttControlBlock> = MaybeUninit::uninit();
139140

141+
#[allow(unused)]
142+
#[export_name = "rtt_init_must_not_be_called_multiple_times"]
143+
fn rtt_init_must_not_be_called_multiple_times() { }
144+
145+
use ::rtt_target::export::critical_section;
146+
147+
static INITIALIZED: critical_section::Mutex<Cell<bool>> = critical_section::Mutex::new(Cell::new(false));
148+
critical_section::with(|cs| {
149+
if INITIALIZED.borrow(cs).get() {
150+
panic!("rtt_init! must not be called multiple times");
151+
}
152+
INITIALIZED.borrow(cs).set(true);
153+
});
154+
140155
unsafe {
141156
ptr::write_bytes(CONTROL_BLOCK.as_mut_ptr(), 0, 1);
142157

rtt-target/src/lib.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@
144144
//! ```
145145
146146
#![no_std]
147+
#![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))]
147148

148149
use core::convert::Infallible;
149150
use core::fmt;
150-
use core::mem::MaybeUninit;
151151
use ufmt_write::uWrite;
152152

153153
#[doc(hidden)]
@@ -234,22 +234,27 @@ impl UpChannel {
234234
///
235235
/// # Safety
236236
///
237+
/// This function must only be called after `rtt_init` has been called.
238+
///
237239
/// It's undefined behavior for something else to access the channel through anything else
238240
/// besides the returned object during or after calling this function. Essentially this function
239241
/// is only safe to use in panic handlers and the like that permanently disable interrupts.
240242
pub unsafe fn conjure(number: usize) -> Option<UpChannel> {
241243
extern "C" {
242244
#[link_name = "_SEGGER_RTT"]
243-
static mut CONTROL_BLOCK: MaybeUninit<rtt::RttHeader>;
245+
static mut CONTROL_BLOCK: rtt::RttHeader;
244246
}
245247

246-
if number >= (*CONTROL_BLOCK.as_ptr()).max_up_channels() {
248+
let control_block = core::ptr::addr_of_mut!(CONTROL_BLOCK);
249+
if number >= (*control_block).max_up_channels() {
247250
return None;
248251
}
249252

253+
let channels = control_block.add(1).cast::<rtt::RttChannel>();
254+
250255
// First addition moves to the start of the up channel array, second addition moves to the
251256
// correct channel.
252-
let ptr = (CONTROL_BLOCK.as_ptr().add(1) as *mut rtt::RttChannel).add(number);
257+
let ptr = channels.add(number);
253258

254259
if !(*ptr).is_initialized() {
255260
return None;
@@ -452,3 +457,10 @@ impl Drop for TerminalWriter<'_> {
452457
}
453458
}
454459
}
460+
461+
/// Used to reexport items for use in macros. Do not use directly.
462+
/// Not covered by semver guarantees.
463+
#[doc(hidden)]
464+
pub mod export {
465+
pub use critical_section;
466+
}

rtt-target/src/rtt.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::ChannelMode;
66
use core::cmp::min;
77
use core::fmt;
88
use core::ptr;
9-
use portable_atomic::{fence, AtomicUsize, Ordering::SeqCst};
9+
use portable_atomic::{AtomicUsize, Ordering::SeqCst};
1010

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

32-
// Copy the ID in two parts to avoid having the ID string in memory in full. The ID is
33-
// copied last to make it less likely an unfinished control block is detected by the host.
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.
3434

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

37-
fence(SeqCst);
38-
39-
ptr::copy_nonoverlapping(
40-
b"ER RTT\0\0\0\0\0\0" as *const u8,
41-
self.id.as_mut_ptr().offset(4),
42-
12,
43-
);
37+
for (idx, byte) in MAGIC_STR_BACKWARDS.into_iter().enumerate() {
38+
ptr::write_volatile(&mut self.id[15 - idx], *byte);
39+
}
4440
}
4541

4642
pub fn max_up_channels(&self) -> usize {

0 commit comments

Comments
 (0)