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