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://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 () => {{
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 = "esp32s3"
155 )
156))]
157mod auto_printer {
158 use crate::{
159 LockToken,
160 serial_jtag_printer::Printer as PrinterSerialJtag,
161 uart_printer::Printer as PrinterUart,
162 };
163
164 pub struct Printer;
165 impl Printer {
166 fn use_jtag() -> bool {
167 #[cfg(feature = "esp32c3")]
174 const USB_DEVICE_INT_RAW: *const u32 = 0x60043008 as *const u32;
175 #[cfg(any(
176 feature = "esp32c5",
177 feature = "esp32c6",
178 feature = "esp32c61",
179 feature = "esp32h2"
180 ))]
181 const USB_DEVICE_INT_RAW: *const u32 = 0x6000f008 as *const u32;
182 #[cfg(feature = "esp32s3")]
183 const USB_DEVICE_INT_RAW: *const u32 = 0x60038000 as *const u32;
184
185 const SOF_INT_MASK: u32 = 0b10;
186
187 unsafe { (USB_DEVICE_INT_RAW.read_volatile() & SOF_INT_MASK) != 0 }
188 }
189
190 pub fn write_bytes_in_cs(bytes: &[u8], token: LockToken<'_>) {
191 if Self::use_jtag() {
192 PrinterSerialJtag::write_bytes_in_cs(bytes, token);
193 } else {
194 PrinterUart::write_bytes_in_cs(bytes, token);
195 }
196 }
197
198 pub fn flush(token: LockToken<'_>) {
199 if Self::use_jtag() {
200 PrinterSerialJtag::flush(token);
201 } else {
202 PrinterUart::flush(token);
203 }
204 }
205 }
206}
207
208#[cfg(all(
209 feature = "auto",
210 not(any(
211 feature = "esp32c3",
212 feature = "esp32c5",
213 feature = "esp32c6",
214 feature = "esp32c61",
215 feature = "esp32h2",
216 feature = "esp32s3"
217 ))
218))]
219mod auto_printer {
220 pub type Printer = crate::uart_printer::Printer;
222}
223
224#[cfg(all(
225 any(feature = "jtag-serial", feature = "auto"),
226 any(
227 feature = "esp32c3",
228 feature = "esp32c5",
229 feature = "esp32c6",
230 feature = "esp32c61",
231 feature = "esp32h2",
232 feature = "esp32s3"
233 )
234))]
235mod serial_jtag_printer {
236 use portable_atomic::{AtomicBool, Ordering};
237
238 use super::LockToken;
239 pub struct Printer;
240
241 #[cfg(feature = "esp32c3")]
242 const SERIAL_JTAG_FIFO_REG: usize = 0x6004_3000;
243 #[cfg(feature = "esp32c3")]
244 const SERIAL_JTAG_CONF_REG: usize = 0x6004_3004;
245
246 #[cfg(any(
247 feature = "esp32c5",
248 feature = "esp32c6",
249 feature = "esp32c61",
250 feature = "esp32h2"
251 ))]
252 const SERIAL_JTAG_FIFO_REG: usize = 0x6000_F000;
253 #[cfg(any(
254 feature = "esp32c5",
255 feature = "esp32c6",
256 feature = "esp32c61",
257 feature = "esp32h2"
258 ))]
259 const SERIAL_JTAG_CONF_REG: usize = 0x6000_F004;
260
261 #[cfg(feature = "esp32s3")]
262 const SERIAL_JTAG_FIFO_REG: usize = 0x6003_8000;
263 #[cfg(feature = "esp32s3")]
264 const SERIAL_JTAG_CONF_REG: usize = 0x6003_8004;
265
266 static TIMED_OUT: AtomicBool = AtomicBool::new(false);
269
270 fn fifo_flush() {
271 let conf = SERIAL_JTAG_CONF_REG as *mut u32;
272 unsafe { conf.write_volatile(0b001) };
273 }
274
275 fn fifo_full() -> bool {
276 let conf = SERIAL_JTAG_CONF_REG as *mut u32;
277 unsafe { conf.read_volatile() & 0b010 == 0b000 }
278 }
279
280 fn fifo_write(byte: u8) {
281 let fifo = SERIAL_JTAG_FIFO_REG as *mut u32;
282 unsafe { fifo.write_volatile(byte as u32) }
283 }
284
285 fn wait_for_flush() -> bool {
286 const TIMEOUT_ITERATIONS: usize = 50_000;
287
288 let mut timeout = TIMEOUT_ITERATIONS;
290 while fifo_full() {
291 if timeout == 0 {
292 TIMED_OUT.store(true, Ordering::Relaxed);
293 return false;
294 }
295 timeout -= 1;
296 }
297
298 true
299 }
300
301 impl Printer {
302 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
303 if fifo_full() {
304 if TIMED_OUT.load(Ordering::Relaxed) {
307 return;
311 }
312
313 if !wait_for_flush() {
315 return;
316 }
317 } else {
318 TIMED_OUT.store(false, Ordering::Relaxed);
320 }
321
322 for &b in bytes {
323 if fifo_full() {
324 fifo_flush();
325
326 if !wait_for_flush() {
328 return;
329 }
330 }
331 fifo_write(b);
332 }
333 }
334
335 pub fn flush(_token: LockToken<'_>) {
336 fifo_flush();
337 }
338 }
339}
340
341#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32"))]
342mod uart_printer {
343 use super::LockToken;
344 const UART_TX_ONE_CHAR: usize = 0x4000_9200;
345
346 pub struct Printer;
347 impl Printer {
348 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
349 for &b in bytes {
350 unsafe {
351 let uart_tx_one_char: unsafe extern "C" fn(u8) -> i32 =
352 core::mem::transmute(UART_TX_ONE_CHAR);
353 uart_tx_one_char(b)
354 };
355 }
356 }
357
358 pub fn flush(_token: LockToken<'_>) {}
359 }
360}
361
362#[cfg(all(any(feature = "uart", feature = "auto"), feature = "esp32s2"))]
363mod uart_printer {
364 use super::LockToken;
365 pub struct Printer;
366 impl Printer {
367 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
368 for chunk in bytes.chunks(64) {
370 for &b in chunk {
371 unsafe {
372 (0x3f400000 as *mut u32).write_volatile(b as u32);
374 };
375 }
376
377 while unsafe { (0x3f400004 as *const u32).read_volatile() } & (1 << 14) == 0 {}
379 unsafe {
380 (0x3f400010 as *mut u32).write_volatile(1 << 14);
382 }
383 }
384 }
385
386 pub fn flush(_token: LockToken<'_>) {}
387 }
388}
389
390#[cfg(all(
391 any(feature = "uart", feature = "auto"),
392 not(any(feature = "esp32", feature = "esp32s2"))
393))]
394mod uart_printer {
395 use super::LockToken;
396 trait Functions {
397 const TX_ONE_CHAR: usize;
398 const CHUNK_SIZE: usize = 32;
399
400 fn tx_byte(b: u8) {
401 unsafe {
402 let tx_one_char: unsafe extern "C" fn(u8) -> i32 =
403 core::mem::transmute(Self::TX_ONE_CHAR);
404 tx_one_char(b);
405 }
406 }
407
408 fn flush();
409 }
410
411 struct Device;
412
413 #[cfg(feature = "esp32c2")]
414 impl Functions for Device {
415 const TX_ONE_CHAR: usize = 0x4000_005C;
416
417 fn flush() {
418 }
420 }
421
422 #[cfg(feature = "esp32c3")]
423 impl Functions for Device {
424 const TX_ONE_CHAR: usize = 0x4000_0068;
425
426 fn flush() {
427 unsafe {
428 const TX_FLUSH: usize = 0x4000_0080;
429 const GET_CHANNEL: usize = 0x4000_058C;
430 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
431 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
432
433 const G_USB_PRINT_ADDR: usize = 0x3FCD_FFD0;
434 let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
435
436 let channel = if *g_usb_print {
437 3
439 } else {
440 get_channel()
441 };
442 tx_flush(channel);
443 }
444 }
445 }
446
447 #[cfg(feature = "esp32s3")]
448 impl Functions for Device {
449 const TX_ONE_CHAR: usize = 0x4000_0648;
450
451 fn flush() {
452 unsafe {
453 const TX_FLUSH: usize = 0x4000_0690;
454 const GET_CHANNEL: usize = 0x4000_1A58;
455 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
456 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
457
458 const G_USB_PRINT_ADDR: usize = 0x3FCE_FFB8;
459 let g_usb_print = G_USB_PRINT_ADDR as *mut bool;
460
461 let channel = if *g_usb_print {
462 4
464 } else {
465 get_channel()
466 };
467 tx_flush(channel);
468 }
469 }
470 }
471
472 #[cfg(any(
473 feature = "esp32c5",
474 feature = "esp32c6",
475 feature = "esp32c61",
476 feature = "esp32h2"
477 ))]
478 impl Functions for Device {
479 const TX_ONE_CHAR: usize = 0x4000_0058;
480
481 fn flush() {
482 unsafe {
483 const TX_FLUSH: usize = 0x4000_0074;
484
485 #[cfg(not(any(feature = "esp32c5", feature = "esp32c61")))]
486 const GET_CHANNEL: usize = 0x4000_003C;
487
488 #[cfg(any(feature = "esp32c5", feature = "esp32c61"))]
489 const GET_CHANNEL: usize = 0x4000_0038;
490
491 let tx_flush: unsafe extern "C" fn(u8) = core::mem::transmute(TX_FLUSH);
492 let get_channel: unsafe extern "C" fn() -> u8 = core::mem::transmute(GET_CHANNEL);
493
494 tx_flush(get_channel());
495 }
496 }
497 }
498
499 pub struct Printer;
500 impl Printer {
501 pub fn write_bytes_in_cs(bytes: &[u8], _token: LockToken<'_>) {
502 for chunk in bytes.chunks(Device::CHUNK_SIZE) {
503 for &b in chunk {
504 Device::tx_byte(b);
505 }
506
507 Device::flush();
508 }
509 }
510
511 pub fn flush(_token: LockToken<'_>) {}
512 }
513}
514
515#[cfg(feature = "no-op")]
516mod noop {
517 pub struct Printer;
518
519 impl Printer {
520 pub fn write_bytes_in_cs(_bytes: &[u8], _token: super::LockToken<'_>) {}
521
522 pub fn flush(_token: super::LockToken<'_>) {}
523 }
524}
525
526use core::marker::PhantomData;
527
528#[derive(Clone, Copy)]
529#[doc(hidden)]
530pub struct LockToken<'a>(PhantomData<&'a ()>);
531
532impl LockToken<'_> {
533 #[allow(unused)]
534 unsafe fn conjure() -> Self {
535 LockToken(PhantomData)
536 }
537}
538
539#[cfg(feature = "critical-section")]
540static LOCK: esp_sync::RawMutex = esp_sync::RawMutex::new();
541
542#[doc(hidden)]
544#[inline]
545pub fn with<R>(f: impl FnOnce(LockToken) -> R) -> R {
546 #[cfg(feature = "critical-section")]
547 return LOCK.lock(|| f(unsafe { LockToken::conjure() }));
548
549 #[cfg(not(feature = "critical-section"))]
550 f(unsafe { LockToken::conjure() })
551}