1
1
//! `defmt` global logger over semihosting
2
2
//!
3
- //! NOTE this is meant to only be used with QEMU
4
- //!
5
- //! WARNING using `cortex_m_semihosting`'s `hprintln!` macro or `HStdout` API will corrupt `defmt`
6
- //! log frames so don't use those APIs.
3
+ //! WARNING using `semihosting`'s `println!` macro or `Stdout` API will corrupt
4
+ //! `defmt` log frames so don't use those APIs.
7
5
//!
8
6
//! # Critical section implementation
9
7
//!
10
- //! This crate uses [`critical-section`](https://github.com/rust-embedded/critical-section) to ensure only one thread
11
- //! is writing to the buffer at a time. You must import a crate that provides a `critical-section` implementation
12
- //! suitable for the current target. See the `critical-section` README for details.
8
+ //! This crate uses
9
+ //! [`critical-section`](https://github.com/rust-embedded/critical-section) to
10
+ //! ensure only one thread is writing to the buffer at a time. You must import a
11
+ //! crate that provides a `critical-section` implementation suitable for the
12
+ //! current target. See the `critical-section` README for details.
13
13
//!
14
- //! For example, for single-core privileged-mode Cortex-M targets, you can add the following to your Cargo.toml.
14
+ //! For example, for single-core privileged-mode Cortex-M targets, you can add
15
+ //! the following to your Cargo.toml.
15
16
//!
16
17
//! ```toml
17
18
//! [dependencies]
18
19
//! cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]}
19
20
//! ```
20
21
21
22
#![ no_std]
22
- // nightly warns about static_mut_refs, but 1.76 (our MSRV) does not know about
23
- // that lint yet, so we ignore it it but also ignore if it is unknown
24
- #![ allow( unknown_lints, static_mut_refs) ]
25
23
26
- use core:: sync:: atomic:: { AtomicBool , Ordering } ;
24
+ use core:: {
25
+ cell:: UnsafeCell ,
26
+ sync:: atomic:: { AtomicBool , Ordering } ,
27
+ } ;
27
28
28
- use cortex_m_semihosting :: hio ;
29
+ use semihosting :: io :: { Stdout , Write as _ } ;
29
30
30
31
#[ defmt:: global_logger]
31
32
struct Logger ;
32
33
33
- static TAKEN : AtomicBool = AtomicBool :: new ( false ) ;
34
- static mut CS_RESTORE : critical_section:: RestoreState = critical_section:: RestoreState :: invalid ( ) ;
35
- static mut ENCODER : defmt:: Encoder = defmt:: Encoder :: new ( ) ;
34
+ static SEMIHOSTING_ENCODER : SemihostingEncoder = SemihostingEncoder :: new ( ) ;
35
+
36
+ struct SemihostingEncoder {
37
+ /// A boolean lock
38
+ ///
39
+ /// Is `true` when `acquire` has been called and we have exclusive access to
40
+ /// the rest of this structure.
41
+ taken : AtomicBool ,
42
+ /// We need to remember this to exit a critical section
43
+ cs_restore : UnsafeCell < critical_section:: RestoreState > ,
44
+ /// A defmt::Encoder for encoding frames
45
+ encoder : UnsafeCell < defmt:: Encoder > ,
46
+ /// A semihosting handle for outputting encoded data
47
+ handle : UnsafeCell < Option < Stdout > > ,
48
+ }
36
49
37
- unsafe impl defmt:: Logger for Logger {
38
- fn acquire ( ) {
50
+ impl SemihostingEncoder {
51
+ /// Create a new semihosting-based defmt-encoder
52
+ const fn new ( ) -> SemihostingEncoder {
53
+ SemihostingEncoder {
54
+ taken : AtomicBool :: new ( false ) ,
55
+ cs_restore : UnsafeCell :: new ( critical_section:: RestoreState :: invalid ( ) ) ,
56
+ encoder : UnsafeCell :: new ( defmt:: Encoder :: new ( ) ) ,
57
+ handle : UnsafeCell :: new ( None ) ,
58
+ }
59
+ }
60
+
61
+ /// Acquire the defmt encoder.
62
+ fn acquire ( & self ) {
39
63
// safety: Must be paired with corresponding call to release(), see below
40
64
let restore = unsafe { critical_section:: acquire ( ) } ;
41
65
42
- if TAKEN . load ( Ordering :: Relaxed ) {
66
+ // NB: You can re-enter critical sections but we need to make sure
67
+ // no-one does that.
68
+ if self . taken . load ( Ordering :: Relaxed ) {
43
69
panic ! ( "defmt logger taken reentrantly" )
44
70
}
45
71
46
- // no need for CAS because interrupts are disabled
47
- TAKEN . store ( true , Ordering :: Relaxed ) ;
72
+ // no need for CAS because we are in a critical section
73
+ self . taken . store ( true , Ordering :: Relaxed ) ;
74
+
75
+ // safety: accessing the cell is OK because we have acquired a critical
76
+ // section.
77
+ unsafe {
78
+ self . cs_restore . get ( ) . write ( restore) ;
79
+ let encoder: & mut defmt:: Encoder = & mut * self . encoder . get ( ) ;
80
+ let handle: & mut Option < Stdout > = & mut * self . handle . get ( ) ;
81
+ if handle. is_none ( ) {
82
+ * handle = semihosting:: io:: stdout ( ) . ok ( ) ;
83
+ }
84
+ encoder. start_frame ( |b| {
85
+ if let Some ( h) = handle {
86
+ _ = h. write_all ( b) ;
87
+ }
88
+ } ) ;
89
+ }
90
+ }
91
+
92
+ /// Release the defmt encoder.
93
+ unsafe fn release ( & self ) {
94
+ if !self . taken . load ( Ordering :: Relaxed ) {
95
+ panic ! ( "defmt release out of context" )
96
+ }
97
+
98
+ // safety: accessing the cell is OK because we have acquired a critical
99
+ // section.
100
+ unsafe {
101
+ let encoder: & mut defmt:: Encoder = & mut * self . encoder . get ( ) ;
102
+ let handle: & mut Option < Stdout > = & mut * self . handle . get ( ) ;
103
+ encoder. end_frame ( |b| {
104
+ if let Some ( h) = handle {
105
+ _ = h. write_all ( b) ;
106
+ }
107
+ } ) ;
108
+ let restore = self . cs_restore . get ( ) . read ( ) ;
109
+ self . taken . store ( false , Ordering :: Relaxed ) ;
110
+ // paired with exactly one acquire call
111
+ critical_section:: release ( restore) ;
112
+ }
113
+ }
114
+
115
+ /// Write bytes to the defmt encoder.
116
+ unsafe fn write ( & self , bytes : & [ u8 ] ) {
117
+ if !self . taken . load ( Ordering :: Relaxed ) {
118
+ panic ! ( "defmt write out of context" )
119
+ }
120
+
121
+ // safety: accessing the cell is OK because we have acquired a critical
122
+ // section.
123
+ unsafe {
124
+ let encoder: & mut defmt:: Encoder = & mut * self . encoder . get ( ) ;
125
+ let handle: & mut Option < Stdout > = & mut * self . handle . get ( ) ;
126
+ encoder. write ( bytes, |b| {
127
+ if let Some ( h) = handle {
128
+ _ = h. write_all ( b) ;
129
+ }
130
+ } ) ;
131
+ }
132
+ }
133
+ }
48
134
49
- // safety: accessing the `static mut` is OK because we have acquired a critical section.
50
- unsafe { CS_RESTORE = restore } ;
135
+ unsafe impl Sync for SemihostingEncoder { }
51
136
52
- // safety: accessing the `static mut` is OK because we have disabled interrupts.
53
- unsafe { ENCODER . start_frame ( do_write) }
137
+ unsafe impl defmt:: Logger for Logger {
138
+ fn acquire ( ) {
139
+ SEMIHOSTING_ENCODER . acquire ( ) ;
54
140
}
55
141
56
142
unsafe fn flush ( ) {
@@ -61,27 +147,14 @@ unsafe impl defmt::Logger for Logger {
61
147
}
62
148
63
149
unsafe fn release ( ) {
64
- // safety: accessing the `static mut` is OK because we have disabled interrupts.
65
- ENCODER . end_frame ( do_write) ;
66
-
67
- TAKEN . store ( false , Ordering :: Relaxed ) ;
68
-
69
- // safety: accessing the `static mut` is OK because we have acquired a critical section.
70
- let restore = unsafe { CS_RESTORE } ;
71
-
72
- // safety: Must be paired with corresponding call to acquire(), see above
73
- unsafe { critical_section:: release ( restore) } ;
150
+ unsafe {
151
+ SEMIHOSTING_ENCODER . release ( ) ;
152
+ }
74
153
}
75
154
76
155
unsafe fn write ( bytes : & [ u8 ] ) {
77
- // safety: accessing the `static mut` is OK because we have disabled interrupts.
78
- ENCODER . write ( bytes, do_write) ;
79
- }
80
- }
81
-
82
- fn do_write ( bytes : & [ u8 ] ) {
83
- // using QEMU; it shouldn't mind us opening several handles (I hope)
84
- if let Ok ( mut hstdout) = hio:: hstdout ( ) {
85
- hstdout. write_all ( bytes) . ok ( ) ;
156
+ unsafe {
157
+ SEMIHOSTING_ENCODER . write ( bytes) ;
158
+ }
86
159
}
87
160
}
0 commit comments