defmt_itm/lib.rs
1//! [`defmt`](https://github.com/knurling-rs/defmt) global logger over ITM.
2//!
3//! To use this crate, call the `enable` function before using the defmt logging macros
4//!
5//! ``` no_run
6//! // src/main.rs or src/bin/my-app.rs
7//!
8//! let p = cortex_m::Peripherals::take().unwrap();
9//! defmt_itm::enable(p.ITM);
10//!
11//! defmt::info!("Hello");
12//! ```
13
14#![doc(html_logo_url = "https://knurling.ferrous-systems.com/knurling_logo_light_text.svg")]
15#![no_std]
16
17use core::sync::atomic::{AtomicBool, Ordering};
18
19use cortex_m::{
20 asm, itm,
21 peripheral::{itm::Stim, ITM},
22};
23
24#[cfg(armv6m)]
25compile_error!(
26 "`defmt-itm` cannot be used on Cortex-M0(+) chips, because it requires an ITM peripheral"
27);
28
29static ENABLED: AtomicBool = AtomicBool::new(false);
30
31/// Enables defmt logging over the ITM stimulus port 0.
32///
33/// This needs to be called by the application before defmt logging is used, otherwise the logs will be disposed.
34pub fn enable(itm: ITM) {
35 // enable stimulus port 0
36 unsafe { itm.ter[0].write(1) }
37 ENABLED.store(true, Ordering::Relaxed);
38}
39
40#[defmt::global_logger]
41struct Logger;
42
43static TAKEN: AtomicBool = AtomicBool::new(false);
44static mut CS_RESTORE: critical_section::RestoreState = critical_section::RestoreState::invalid();
45static mut ENCODER: defmt::Encoder = defmt::Encoder::new();
46
47unsafe impl defmt::Logger for Logger {
48 fn acquire() {
49 // safety: Must be paired with corresponding call to release(), see below
50 let restore = unsafe { critical_section::acquire() };
51
52 // safety: accessing the atomic without CAS is OK because we have acquired a critical section.
53 if TAKEN.load(Ordering::Relaxed) {
54 panic!("defmt logger taken reentrantly")
55 }
56
57 // safety: accessing the atomic without CAS is OK because we have acquired a critical section.
58 TAKEN.store(true, Ordering::Relaxed);
59
60 // safety: accessing the `static mut` is OK because we have acquired a critical section.
61 unsafe { CS_RESTORE = restore };
62
63 // safety: accessing the `static mut` is OK because we have acquired a critical section.
64 unsafe {
65 let encoder: &mut defmt::Encoder = &mut *core::ptr::addr_of_mut!(ENCODER);
66 encoder.start_frame(do_write)
67 }
68 }
69
70 unsafe fn flush() {
71 // wait for the queue to be able to accept more data
72 while !stim_0().is_fifo_ready() {}
73
74 // delay "a bit" to drain the queue
75 // This is a heuristic and might be too short in reality. Please open an issue if it is!
76 asm::delay(100);
77 }
78
79 unsafe fn release() {
80 // safety: accessing the `static mut` is OK because we have acquired a critical section.
81 unsafe {
82 let encoder: &mut defmt::Encoder = &mut *core::ptr::addr_of_mut!(ENCODER);
83 encoder.end_frame(do_write);
84 }
85
86 // safety: accessing the atomic without CAS is OK because we have acquired a critical section.
87 TAKEN.store(false, Ordering::Relaxed);
88
89 // safety: accessing the `static mut` is OK because we have acquired a critical section.
90 let restore = unsafe { CS_RESTORE };
91
92 // safety: Must be paired with corresponding call to acquire(), see above
93 unsafe {
94 critical_section::release(restore);
95 }
96 }
97
98 unsafe fn write(bytes: &[u8]) {
99 // safety: accessing the `static mut` is OK because we have acquired a critical section.
100 unsafe {
101 let encoder: &mut defmt::Encoder = &mut *core::ptr::addr_of_mut!(ENCODER);
102 encoder.write(bytes, do_write);
103 }
104 }
105}
106
107fn do_write(bytes: &[u8]) {
108 // NOTE(unsafe) this function will be invoked *after* `enable` has run so this crate now has
109 // ownership over the ITM thus it's OK to instantiate the ITM register block here
110 unsafe { itm::write_all(stim_0(), bytes) }
111}
112
113/// Get access to stimulus port 0
114///
115/// # Safety
116/// Can only be invoked *after* `enable` has run
117unsafe fn stim_0<'a>() -> &'a mut Stim {
118 &mut (*ITM::PTR).stim[0]
119}