1#![doc = include_str!("../README.md")]
2#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
4#![doc(html_logo_url = "https://docs.espressif.com/projects/rust/esp-rs-grey-bg.svg")]
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 () => {{
42 $crate::Printer::write_bytes(&[b'\n']);
43 }};
44 ($($arg:tt)*) => {{
45 fn _do_print(args: ::core::fmt::Arguments<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {
46 $crate::with(|_| {
47 use ::core::fmt::Write;
48 ($crate::Printer).write_fmt(args)?;
49 $crate::Printer::write_bytes(&[b'\n']);
50 Ok(())
51 })
52 }
53 _do_print(::core::format_args!($($arg)*)).ok();
54 }};
55}
56
57#[cfg(not(feature = "no-op"))]
59#[macro_export]
60macro_rules! print {
61 ($($arg:tt)*) => {{
62 fn _do_print(args: ::core::fmt::Arguments<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {
63 $crate::with(|_| {
64 use ::core::fmt::Write;
65 ($crate::Printer).write_fmt(args)
66 })
67 }
68 _do_print(::core::format_args!($($arg)*)).ok();
69 }};
70}
71
72#[cfg(feature = "no-op")]
74#[macro_export]
75macro_rules! println {
76 ($($arg:tt)*) => {{}};
77}
78
79#[cfg(feature = "no-op")]
81#[macro_export]
82macro_rules! print {
83 ($($arg:tt)*) => {{}};
84}
85
86#[macro_export]
90macro_rules! dbg {
91 () => {
96 $crate::println!("[{}:{}]", ::core::file!(), ::core::line!())
97 };
98 ($val:expr $(,)?) => {
99 match $val {
102 tmp => {
103 $crate::println!("[{}:{}] {} = {:#?}",
104 ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
105 tmp
106 }
107 }
108 };
109 ($($val:expr),+ $(,)?) => {
110 ($($crate::dbg!($val)),+,)
111 };
112}
113
114pub struct Printer;
116
117impl core::fmt::Write for Printer {
118 fn write_str(&mut self, s: &str) -> core::fmt::Result {
119 Printer::write_bytes(s.as_bytes());
120 Ok(())
121 }
122}
123
124impl Printer {
125 pub fn write_bytes(bytes: &[u8]) {
127 with(|token| {
128 PrinterImpl::write_bytes_in_cs(bytes, token);
129 PrinterImpl::flush(token);
130 })
131 }
132}
133
134#[cfg(feature = "jtag-serial")]
135type PrinterImpl = serial_jtag_printer::Printer;
136
137#[cfg(feature = "uart")]
138type PrinterImpl = uart_printer::Printer;
139
140#[cfg(feature = "auto")]
141type PrinterImpl = auto_printer::Printer;
142
143#[cfg(feature = "no-op")]
144type PrinterImpl = noop::Printer;
145
146#[cfg(all(
147 feature = "auto",
148 any(
149 feature = "esp32c3",
150 feature = "esp32c5",
151 feature = "esp32c6",
152 feature = "esp32c61",
153 feature = "esp32h2",
154 feature = "esp32p4",
155 feature = "esp32s3",
156 feature = "esp32s31"
157 )
158))]
159mod auto_printer {
160 use crate::{
161 LockToken,
162 serial_jtag_printer::Printer as PrinterSerialJtag,
163 uart_printer::Printer as PrinterUart,
164 };
165
166 pub struct Printer;
167 impl Printer {
168 fn use_jtag() -> bool {
169 #[cfg(feature = "esp32c3")]
176 const USB_DEVICE_INT_RAW: *const u32 = 0x60043008 as *const u32;
177 #[cfg(any(
178 feature = "esp32c5",
179 feature = "esp32c6",
180 feature = "esp32c61",
181 feature = "esp32h2"
182 ))]
183 const USB_DEVICE_INT_RAW: *const u32 = 0x6000f008 as *const u32;
184 #[cfg(feature = "esp32s3")]
185 const USB_DEVICE_INT_RAW: *const u32 = 0x60038000 as *const u32;
186 #[cfg(feature = "esp32p4")]
187 const USB_DEVICE_INT_RAW: *const u32 = 0x500D2008 as *const u32;
188 #[cfg(feature = "esp32s31")]
189 const USB_DEVICE_INT_RAW: *const u32 = 0x20391008 as *const u32;
190
191 const SOF_INT_MASK: u32 = 0b10;
192
193 unsafe { (USB_DEVICE_INT_RAW.read_volatile() & SOF_INT_MASK) != 0 }
194 }
195
196 pub fn write_bytes_in_cs(bytes: &[u8], token: LockToken<'_>) {
197 if Self::use_jtag() {
198 PrinterSerialJtag::write_bytes_in_cs(bytes, token);
199 } else {
200 PrinterUart::write_bytes_in_cs(bytes, token);
201 }
202 }
203
204 pub fn flush(token: LockToken<'_>) {
205 if Self::use_jtag() {
206 PrinterSerialJtag::flush(token);
207 } else {
208 PrinterUart::flush(token);
209 }
210 }
211 }
212}
213
214#[cfg(all(
215 feature = "auto",
216 not(any(
217 feature = "esp32c3",
218 feature = "esp32c5",
219 feature = "esp32c6",
220 feature = "esp32c61",
221 feature = "esp32h2",
222 feature = "esp32p4",
223 feature = "esp32s3",
224 feature = "esp32s31"
225 ))
226))]
227mod auto_printer {
228 pub type Printer = crate::uart_printer::Printer;
230}
231
232#[cfg(all(
233 any(feature = "jtag-serial", feature = "auto"),
234 any(
235 feature = "esp32c3",
236 feature = "esp32c5",
237 feature = "esp32c6",
238 feature = "esp32c61",
239 feature = "esp32h2",
240 feature = "esp32p4",
241 feature = "esp32s3",
242 feature = "esp32s31"
243 )
244))]
245mod serial_jtag_printer {
246 use portable_atomic::{AtomicBool, Ordering};
247
248 use super::LockToken;
249 pub struct Printer;
250
251 #[cfg(feature = "esp32c3")]
252 const SERIAL_JTAG_FIFO_REG: usize = 0x6004_3000;
253 #[cfg(feature = "esp32c3")]
254 const SERIAL_JTAG_CONF_REG: usize = 0x6004_3004;
255
256 #[cfg(any(
257 feature = "esp32c5",
258 feature = "esp32c6",
259 feature = "esp32c61",
260 feature = "esp32h2"
261 ))]
262 const SERIAL_JTAG_FIFO_REG: usize = 0x6000_F000;
263 #[cfg(any(
264 feature = "esp32c5",
265 feature = "esp32c6",
266 feature = "esp32c61",
267 feature = "esp32h2"
268 ))]
269 const SERIAL_JTAG_CONF_REG: usize = 0x6000_F004;
270
271 #[cfg(feature = "esp32s3")]
272 const SERIAL_JTAG_FIFO_REG: usize = 0x6003_8000;
273 #[cfg(feature = "esp32s3")]
274 const SERIAL_JTAG_CONF_REG: usize = 0x6003_8004;
275
276 #[cfg(feature = "esp32p4")]
278 const SERIAL_JTAG_FIFO_REG: usize = 0x500D_2000;
279 #[cfg(feature = "esp32p4")]
280 const SERIAL_JTAG_CONF_REG: usize = 0x500D_2004;
281
282 #[cfg(feature = "esp32s31")]
284 const SERIAL_JTAG_FIFO_REG: usize = 0x2039_1000;
285 #[cfg(feature = "esp32s31")]
286 const SERIAL_JTAG_CONF_REG: usize = 0x2039_1004;
287
288 static TIMED_OUT: AtomicBool = AtomicBool::new(false);
291
292 fn fifo_flush() {
293 let conf = SERIAL_JTAG_CONF_REG as *mut u32;
294 unsafe { conf.write_volatile(0b001) };
295 }
296
297 fn fifo_full() -> bool {
298 let conf = SERIAL_JTAG_CONF_REG as *mut u32;
299 unsafe { conf.read_volatile() & 0b010 == 0b000 }
300 }
301
302 fn fifo_write(byte: u8) {
303 let fifo = SERIAL_JTAG_FIFO_REG as *mut u32;
304 unsafe { fifo.write_volatile(byte as u32) }
305 }
306
307 fn wait_for_flush() -> bool {
308 const TIMEOUT_ITERATIONS: usize = 50_000;
309
310 let mut timeout = TIMEOUT_ITERATIONS;
312 while fifo_full() {
313 if timeout == 0 {
314 TIMED_OUT.store(true, Ordering::Relaxed);
315 return false;
316 }
317 timeout -= 1;
318 }
319
320 true
321 }
322
323 impl Printer {
324 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
325 if fifo_full() {
326 if TIMED_OUT.load(Ordering::Relaxed) {
329 return;
333 }
334
335 if !wait_for_flush() {
337 return;
338 }
339 } else {
340 TIMED_OUT.store(false, Ordering::Relaxed);
342 }
343
344 for &b in bytes {
345 if fifo_full() {
346 fifo_flush();
347
348 if !wait_for_flush() {
350 return;
351 }
352 }
353 fifo_write(b);
354 }
355 }
356
357 pub fn flush(_token: LockToken<'_>) {
358 fifo_flush();
359 }
360 }
361}
362
363#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32"))]
364mod uart_printer {
365 use super::LockToken;
366 const UART_TX_ONE_CHAR: usize = 0x4000_9200;
367
368 pub struct Printer;
369 impl Printer {
370 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
371 for &b in bytes {
372 unsafe {
373 let uart_tx_one_char: unsafe extern "C" fn(u8) -> i32 =
374 core::mem::transmute(UART_TX_ONE_CHAR);
375 uart_tx_one_char(b)
376 };
377 }
378 }
379
380 pub fn flush(_token: LockToken<'_>) {}
381 }
382}
383
384#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32s2"))]
385mod uart_printer {
386 use super::LockToken;
387 pub struct Printer;
388 impl Printer {
389 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
390 for chunk in bytes.chunks(64) {
392 for &b in chunk {
393 unsafe {
394 (0x3f400000 as *mut u32).write_volatile(b as u32);
396 };
397 }
398
399 while unsafe { (0x3f400004 as *const u32).read_volatile() } & (1 << 14) == 0 {}
401 unsafe {
402 (0x3f400010 as *mut u32).write_volatile(1 << 14);
404 }
405 }
406 }
407
408 pub fn flush(_token: LockToken<'_>) {}
409 }
410}
411
412#[cfg(all(
413 any(feature = "uart", feature = "auto"),
414 not(any(feature = "esp32", feature = "esp32s2"))
415))]
416mod uart_printer {
417 use super::LockToken;
418 trait Functions {
419 const TX_ONE_CHAR: usize;
420 const CHUNK_SIZE: usize = 32;
421
422 fn tx_byte(b: u8) {
423 unsafe {
424 let tx_one_char: unsafe extern "C" fn(u8) -> i32 =
425 core::mem::transmute(Self::TX_ONE_CHAR);
426 tx_one_char(b);
427 }
428 }
429
430 fn flush();
431 }
432
433 struct Device;
434
435 #[cfg(any(feature = "esp32p4", feature = "esp32s31"))]
440 impl Functions for Device {
441 const TX_ONE_CHAR: usize = 0;
443
444 fn tx_byte(b: u8) {
445 unsafe extern "C" {
446 fn esp_rom_uart_tx_one_char(c: u8) -> i32;
447 }
448 unsafe {
449 esp_rom_uart_tx_one_char(b);
450 }
451 }
452
453 fn flush() {
454 }
456 }
457
458 #[cfg(feature = "esp32c2")]
459 impl Functions for Device {
460 const TX_ONE_CHAR: usize = 0x4000_005C;
461
462 fn flush() {
463 }
465 }
466
467 #[cfg(feature = "esp32c3")]
468 impl Functions for Device {
469 const TX_ONE_CHAR: usize = 0x4000_0068;
470
471 fn flush() {
472 unsafe {
473 const TX_FLUSH: usize = 0x4000_0080;
474 const GET_CHANNEL: usize = 0x4000_058C;
475 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
476 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
477
478 const G_USB_PRINT_ADDR: usize = 0x3FCD_FFD0;
479 let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
480
481 let channel = if *g_usb_print {
482 3
484 } else {
485 get_channel()
486 };
487 tx_flush(channel);
488 }
489 }
490 }
491
492 #[cfg(feature = "esp32s3")]
493 impl Functions for Device {
494 const TX_ONE_CHAR: usize = 0x4000_0648;
495
496 fn flush() {
497 unsafe {
498 const TX_FLUSH: usize = 0x4000_0690;
499 const GET_CHANNEL: usize = 0x4000_1A58;
500 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
501 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
502
503 const G_USB_PRINT_ADDR: usize = 0x3FCE_FFB8;
504 let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
505
506 let channel = if *g_usb_print {
507 4
509 } else {
510 get_channel()
511 };
512 tx_flush(channel);
513 }
514 }
515 }
516
517 #[cfg(any(
518 feature = "esp32c5",
519 feature = "esp32c6",
520 feature = "esp32c61",
521 feature = "esp32h2"
522 ))]
523 impl Functions for Device {
524 const TX_ONE_CHAR: usize = 0x4000_0058;
525
526 fn flush() {
527 unsafe {
528 const TX_FLUSH: usize = 0x4000_0074;
529
530 #[cfg(not(any(feature = "esp32c5", feature = "esp32c61")))]
531 const GET_CHANNEL: usize = 0x4000_003C;
532
533 #[cfg(any(feature = "esp32c5", feature = "esp32c61"))]
534 const GET_CHANNEL: usize = 0x4000_0038;
535
536 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
537 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
538
539 tx_flush(get_channel());
540 }
541 }
542 }
543
544 pub struct Printer;
545 impl Printer {
546 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
547 for chunk in bytes.chunks(Device::CHUNK_SIZE) {
548 for &b in chunk {
549 Device::tx_byte(b);
550 }
551
552 Device::flush();
553 }
554 }
555
556 pub fn flush(_token: LockToken<'_>) {}
557 }
558}
559
560#[cfg(feature = "no-op")]
561mod noop {
562 pub struct Printer;
563
564 impl Printer {
565 pub fn write_bytes_in_cs(_bytes: &[u8], _token: super::LockToken<'_>) {}
566
567 pub fn flush(_token: super::LockToken<'_>) {}
568 }
569}
570
571use core::marker::PhantomData;
572
573#[derive(Clone, Copy)]
574#[doc(hidden)]
575pub struct LockToken<'a>(PhantomData<&'a ()>);
576
577impl LockToken<'_> {
578 #[allow(unused)]
579 unsafe fn conjure() -> Self {
580 LockToken(PhantomData)
581 }
582}
583
584#[cfg(feature = "critical-section")]
585static LOCK: esp_sync::RawMutex = esp_sync::RawMutex::new();
586
587#[doc(hidden)]
589#[inline]
590pub fn with<R>(f: impl FnOnce(LockToken) -> R) -> R {
591 #[cfg(feature = "critical-section")]
592 return LOCK.lock(|| f(unsafe { LockToken::conjure() }));
593
594 #[cfg(not(feature = "critical-section"))]
595 f(unsafe { LockToken::conjure() })
596}