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}