1#![doc = include_str!("../README.md")]
2#![doc = document_features::document_features!()]
4#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
5#![allow(rustdoc::bare_urls)]
6#![no_std]
7
8#[cfg(feature = "defmt-espflash")]
9pub mod defmt;
10#[cfg(feature = "log-04")]
11pub mod logger;
12
13macro_rules! log_format {
14 ($value:expr) => {
15 #[unsafe(link_section = concat!(".espressif.metadata"))]
16 #[used]
17 #[unsafe(export_name = concat!("espflash.LOG_FORMAT"))]
18 static LOG_FORMAT: [u8; $value.len()] = const {
19 let val_bytes = $value.as_bytes();
20 let mut val_bytes_array = [0; $value.len()];
21 let mut i = 0;
22 while i < val_bytes.len() {
23 val_bytes_array[i] = val_bytes[i];
24 i += 1;
25 }
26 val_bytes_array
27 };
28 };
29}
30
31#[cfg(feature = "defmt-espflash")]
32log_format!("defmt-espflash");
33
34#[cfg(not(feature = "defmt-espflash"))]
35log_format!("serial");
36
37#[cfg(not(feature = "no-op"))]
39#[macro_export]
40macro_rules! println {
41 ($($arg:tt)*) => {{
42 {
43 use core::fmt::Write;
44 writeln!($crate::Printer, $($arg)*).ok();
45 }
46 }};
47}
48
49#[cfg(not(feature = "no-op"))]
51#[macro_export]
52macro_rules! print {
53 ($($arg:tt)*) => {{
54 {
55 use core::fmt::Write;
56 write!($crate::Printer, $($arg)*).ok();
57 }
58 }};
59}
60
61#[cfg(feature = "no-op")]
63#[macro_export]
64macro_rules! println {
65 ($($arg:tt)*) => {{}};
66}
67
68#[cfg(feature = "no-op")]
70#[macro_export]
71macro_rules! print {
72 ($($arg:tt)*) => {{}};
73}
74
75#[macro_export]
79macro_rules! dbg {
80 () => {
85 $crate::println!("[{}:{}]", ::core::file!(), ::core::line!())
86 };
87 ($val:expr $(,)?) => {
88 match $val {
91 tmp => {
92 $crate::println!("[{}:{}] {} = {:#?}",
93 ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
94 tmp
95 }
96 }
97 };
98 ($($val:expr),+ $(,)?) => {
99 ($($crate::dbg!($val)),+,)
100 };
101}
102
103pub struct Printer;
105
106impl core::fmt::Write for Printer {
107 fn write_str(&mut self, s: &str) -> core::fmt::Result {
108 Printer::write_bytes(s.as_bytes());
109 Ok(())
110 }
111}
112
113impl Printer {
114 pub fn write_bytes(bytes: &[u8]) {
116 with(|token| {
117 PrinterImpl::write_bytes_in_cs(bytes, token);
118 PrinterImpl::flush(token);
119 })
120 }
121}
122
123#[cfg(feature = "jtag-serial")]
124type PrinterImpl = serial_jtag_printer::Printer;
125
126#[cfg(feature = "uart")]
127type PrinterImpl = uart_printer::Printer;
128
129#[cfg(feature = "auto")]
130type PrinterImpl = auto_printer::Printer;
131
132#[cfg(all(
133 feature = "auto",
134 any(
135 feature = "esp32c3",
136 feature = "esp32c6",
137 feature = "esp32h2",
138 feature = "esp32s3"
139 )
140))]
141mod auto_printer {
142 use crate::{
143 LockToken,
144 serial_jtag_printer::Printer as PrinterSerialJtag,
145 uart_printer::Printer as PrinterUart,
146 };
147
148 pub struct Printer;
149 impl Printer {
150 fn use_jtag() -> bool {
151 #[cfg(feature = "esp32c3")]
158 const USB_DEVICE_INT_RAW: *const u32 = 0x60043008 as *const u32;
159 #[cfg(feature = "esp32c6")]
160 const USB_DEVICE_INT_RAW: *const u32 = 0x6000f008 as *const u32;
161 #[cfg(feature = "esp32h2")]
162 const USB_DEVICE_INT_RAW: *const u32 = 0x6000f008 as *const u32;
163 #[cfg(feature = "esp32s3")]
164 const USB_DEVICE_INT_RAW: *const u32 = 0x60038000 as *const u32;
165
166 const SOF_INT_MASK: u32 = 0b10;
167
168 unsafe { (USB_DEVICE_INT_RAW.read_volatile() & SOF_INT_MASK) != 0 }
169 }
170
171 pub fn write_bytes_in_cs(bytes: &[u8], token: LockToken<'_>) {
172 if Self::use_jtag() {
173 PrinterSerialJtag::write_bytes_in_cs(bytes, token);
174 } else {
175 PrinterUart::write_bytes_in_cs(bytes, token);
176 }
177 }
178
179 pub fn flush(token: LockToken<'_>) {
180 if Self::use_jtag() {
181 PrinterSerialJtag::flush(token);
182 } else {
183 PrinterUart::flush(token);
184 }
185 }
186 }
187}
188
189#[cfg(all(
190 feature = "auto",
191 not(any(
192 feature = "esp32c3",
193 feature = "esp32c6",
194 feature = "esp32h2",
195 feature = "esp32s3"
196 ))
197))]
198mod auto_printer {
199 pub type Printer = crate::uart_printer::Printer;
201}
202
203#[cfg(all(
204 any(feature = "jtag-serial", feature = "auto"),
205 any(
206 feature = "esp32c3",
207 feature = "esp32c6",
208 feature = "esp32h2",
209 feature = "esp32s3"
210 )
211))]
212mod serial_jtag_printer {
213 use portable_atomic::{AtomicBool, Ordering};
214
215 use super::LockToken;
216 pub struct Printer;
217
218 #[cfg(feature = "esp32c3")]
219 const SERIAL_JTAG_FIFO_REG: usize = 0x6004_3000;
220 #[cfg(feature = "esp32c3")]
221 const SERIAL_JTAG_CONF_REG: usize = 0x6004_3004;
222
223 #[cfg(any(feature = "esp32c6", feature = "esp32h2"))]
224 const SERIAL_JTAG_FIFO_REG: usize = 0x6000_F000;
225 #[cfg(any(feature = "esp32c6", feature = "esp32h2"))]
226 const SERIAL_JTAG_CONF_REG: usize = 0x6000_F004;
227
228 #[cfg(feature = "esp32s3")]
229 const SERIAL_JTAG_FIFO_REG: usize = 0x6003_8000;
230 #[cfg(feature = "esp32s3")]
231 const SERIAL_JTAG_CONF_REG: usize = 0x6003_8004;
232
233 static TIMED_OUT: AtomicBool = AtomicBool::new(false);
236
237 fn fifo_flush() {
238 let conf = SERIAL_JTAG_CONF_REG as *mut u32;
239 unsafe { conf.write_volatile(0b001) };
240 }
241
242 fn fifo_full() -> bool {
243 let conf = SERIAL_JTAG_CONF_REG as *mut u32;
244 unsafe { conf.read_volatile() & 0b010 == 0b000 }
245 }
246
247 fn fifo_write(byte: u8) {
248 let fifo = SERIAL_JTAG_FIFO_REG as *mut u32;
249 unsafe { fifo.write_volatile(byte as u32) }
250 }
251
252 fn wait_for_flush() -> bool {
253 const TIMEOUT_ITERATIONS: usize = 50_000;
254
255 let mut timeout = TIMEOUT_ITERATIONS;
257 while fifo_full() {
258 if timeout == 0 {
259 TIMED_OUT.store(true, Ordering::Relaxed);
260 return false;
261 }
262 timeout -= 1;
263 }
264
265 true
266 }
267
268 impl Printer {
269 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
270 if fifo_full() {
271 if TIMED_OUT.load(Ordering::Relaxed) {
274 return;
278 }
279
280 if !wait_for_flush() {
282 return;
283 }
284 } else {
285 TIMED_OUT.store(false, Ordering::Relaxed);
287 }
288
289 for &b in bytes {
290 if fifo_full() {
291 fifo_flush();
292
293 if !wait_for_flush() {
295 return;
296 }
297 }
298 fifo_write(b);
299 }
300 }
301
302 pub fn flush(_token: LockToken<'_>) {
303 fifo_flush();
304 }
305 }
306}
307
308#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32"))]
309mod uart_printer {
310 use super::LockToken;
311 const UART_TX_ONE_CHAR: usize = 0x4000_9200;
312
313 pub struct Printer;
314 impl Printer {
315 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
316 for &b in bytes {
317 unsafe {
318 let uart_tx_one_char: unsafe extern "C" fn(u8) -> i32 =
319 core::mem::transmute(UART_TX_ONE_CHAR);
320 uart_tx_one_char(b)
321 };
322 }
323 }
324
325 pub fn flush(_token: LockToken<'_>) {}
326 }
327}
328
329#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32s2"))]
330mod uart_printer {
331 use super::LockToken;
332 pub struct Printer;
333 impl Printer {
334 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
335 for chunk in bytes.chunks(64) {
337 for &b in chunk {
338 unsafe {
339 (0x3f400000 as *mut u32).write_volatile(b as u32);
341 };
342 }
343
344 while unsafe { (0x3f400004 as *const u32).read_volatile() } & (1 << 14) == 0 {}
346 unsafe {
347 (0x3f400010 as *mut u32).write_volatile(1 << 14);
349 }
350 }
351 }
352
353 pub fn flush(_token: LockToken<'_>) {}
354 }
355}
356
357#[cfg(all(
358 any(feature = "uart", feature = "auto"),
359 not(any(feature = "esp32", feature = "esp32s2"))
360))]
361mod uart_printer {
362 use super::LockToken;
363 trait Functions {
364 const TX_ONE_CHAR: usize;
365 const CHUNK_SIZE: usize = 32;
366
367 fn tx_byte(b: u8) {
368 unsafe {
369 let tx_one_char: unsafe extern "C" fn(u8) -> i32 =
370 core::mem::transmute(Self::TX_ONE_CHAR);
371 tx_one_char(b);
372 }
373 }
374
375 fn flush();
376 }
377
378 struct Device;
379
380 #[cfg(feature = "esp32c2")]
381 impl Functions for Device {
382 const TX_ONE_CHAR: usize = 0x4000_005C;
383
384 fn flush() {
385 }
387 }
388
389 #[cfg(feature = "esp32c3")]
390 impl Functions for Device {
391 const TX_ONE_CHAR: usize = 0x4000_0068;
392
393 fn flush() {
394 unsafe {
395 const TX_FLUSH: usize = 0x4000_0080;
396 const GET_CHANNEL: usize = 0x4000_058C;
397 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
398 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
399
400 const G_USB_PRINT_ADDR: usize = 0x3FCD_FFD0;
401 let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
402
403 let channel = if *g_usb_print {
404 3
406 } else {
407 get_channel()
408 };
409 tx_flush(channel);
410 }
411 }
412 }
413
414 #[cfg(feature = "esp32s3")]
415 impl Functions for Device {
416 const TX_ONE_CHAR: usize = 0x4000_0648;
417
418 fn flush() {
419 unsafe {
420 const TX_FLUSH: usize = 0x4000_0690;
421 const GET_CHANNEL: usize = 0x4000_1A58;
422 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
423 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
424
425 const G_USB_PRINT_ADDR: usize = 0x3FCE_FFB8;
426 let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
427
428 let channel = if *g_usb_print {
429 4
431 } else {
432 get_channel()
433 };
434 tx_flush(channel);
435 }
436 }
437 }
438
439 #[cfg(any(feature = "esp32c6", feature = "esp32h2"))]
440 impl Functions for Device {
441 const TX_ONE_CHAR: usize = 0x4000_0058;
442
443 fn flush() {
444 unsafe {
445 const TX_FLUSH: usize = 0x4000_0074;
446 const GET_CHANNEL: usize = 0x4000_003C;
447
448 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
449 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
450
451 tx_flush(get_channel());
452 }
453 }
454 }
455
456 pub struct Printer;
457 impl Printer {
458 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
459 for chunk in bytes.chunks(Device::CHUNK_SIZE) {
460 for &b in chunk {
461 Device::tx_byte(b);
462 }
463
464 Device::flush();
465 }
466 }
467
468 pub fn flush(_token: LockToken<'_>) {}
469 }
470}
471
472#[cfg(not(feature = "critical-section"))]
473use core::marker::PhantomData;
474
475#[cfg(not(feature = "critical-section"))]
476type LockInner<'a> = PhantomData<&'a ()>;
477#[cfg(feature = "critical-section")]
478type LockInner<'a> = critical_section::CriticalSection<'a>;
479
480#[derive(Clone, Copy)]
481struct LockToken<'a>(LockInner<'a>);
482
483impl LockToken<'_> {
484 #[allow(unused)]
485 unsafe fn conjure() -> Self {
486 unsafe {
487 #[cfg(feature = "critical-section")]
488 let inner = critical_section::CriticalSection::new();
489 #[cfg(not(feature = "critical-section"))]
490 let inner = PhantomData;
491
492 LockToken(inner)
493 }
494 }
495}
496
497#[inline]
499fn with<R>(f: impl FnOnce(LockToken) -> R) -> R {
500 #[cfg(feature = "critical-section")]
501 return critical_section::with(|cs| f(LockToken(cs)));
502
503 #[cfg(not(feature = "critical-section"))]
504 f(unsafe { LockToken::conjure() })
505}