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