Skip to main content

oxigdal_embedded/target/
esp32.rs

1//! ESP32 specific optimizations and support
2//!
3//! Provides implementations for ESP32, ESP32-S2, ESP32-S3, ESP32-C3, and ESP32-C6
4
5use super::{TargetArch, TargetCapabilities};
6use core::sync::atomic::{Ordering, fence};
7
8/// ESP32 variant
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Esp32Variant {
11    /// ESP32 (Xtensa LX6)
12    Esp32,
13    /// ESP32-S2 (Xtensa LX7)
14    Esp32S2,
15    /// ESP32-S3 (Xtensa LX7, dual-core)
16    Esp32S3,
17    /// ESP32-C3 (RISC-V)
18    Esp32C3,
19    /// ESP32-C6 (RISC-V)
20    Esp32C6,
21    /// ESP32-H2 (RISC-V)
22    Esp32H2,
23}
24
25/// ESP32 target implementation
26pub struct Esp32Target {
27    variant: Esp32Variant,
28}
29
30impl Esp32Target {
31    /// Create a new ESP32 target
32    pub const fn new(variant: Esp32Variant) -> Self {
33        Self { variant }
34    }
35
36    /// Get the ESP32 variant
37    pub const fn variant(&self) -> Esp32Variant {
38        self.variant
39    }
40
41    /// Check if this is an Xtensa-based ESP32
42    pub const fn is_xtensa(&self) -> bool {
43        matches!(
44            self.variant,
45            Esp32Variant::Esp32 | Esp32Variant::Esp32S2 | Esp32Variant::Esp32S3
46        )
47    }
48
49    /// Check if this is a RISC-V based ESP32
50    pub const fn is_riscv(&self) -> bool {
51        matches!(
52            self.variant,
53            Esp32Variant::Esp32C3 | Esp32Variant::Esp32C6 | Esp32Variant::Esp32H2
54        )
55    }
56}
57
58impl TargetArch for Esp32Target {
59    fn name(&self) -> &'static str {
60        match self.variant {
61            Esp32Variant::Esp32 => "ESP32",
62            Esp32Variant::Esp32S2 => "ESP32-S2",
63            Esp32Variant::Esp32S3 => "ESP32-S3",
64            Esp32Variant::Esp32C3 => "ESP32-C3",
65            Esp32Variant::Esp32C6 => "ESP32-C6",
66            Esp32Variant::Esp32H2 => "ESP32-H2",
67        }
68    }
69
70    fn pointer_size(&self) -> usize {
71        4 // All ESP32 variants are 32-bit
72    }
73
74    fn native_alignment(&self) -> usize {
75        4 // 32-bit alignment
76    }
77
78    fn supports_unaligned_access(&self) -> bool {
79        // Xtensa supports unaligned access with performance penalty
80        // RISC-V variants do not
81        self.is_xtensa()
82    }
83
84    fn memory_barrier(&self) {
85        memory_barrier();
86    }
87
88    fn cycle_count(&self) -> Option<u64> {
89        cycle_count(self.variant)
90    }
91}
92
93/// Get ESP32 target capabilities
94pub fn get_capabilities() -> TargetCapabilities {
95    let variant = detect_variant();
96
97    TargetCapabilities {
98        has_fpu: true, // All ESP32 variants have FPU
99        has_simd: false,
100        has_aes: matches!(
101            variant,
102            Esp32Variant::Esp32 | Esp32Variant::Esp32S3 | Esp32Variant::Esp32C6
103        ),
104        has_crc: false,
105        cache_line_size: 32, // ESP32 cache line size
106        num_cores: match variant {
107            Esp32Variant::Esp32 | Esp32Variant::Esp32S3 => 2,
108            _ => 1,
109        },
110    }
111}
112
113/// Detect ESP32 variant at compile time
114pub const fn detect_variant() -> Esp32Variant {
115    #[cfg(all(target_arch = "xtensa", esp32))]
116    {
117        Esp32Variant::Esp32
118    }
119
120    #[cfg(all(target_arch = "xtensa", esp32s2))]
121    {
122        Esp32Variant::Esp32S2
123    }
124
125    #[cfg(all(target_arch = "xtensa", esp32s3))]
126    {
127        Esp32Variant::Esp32S3
128    }
129
130    #[cfg(all(target_arch = "riscv32", esp32c3))]
131    {
132        Esp32Variant::Esp32C3
133    }
134
135    #[cfg(all(target_arch = "riscv32", esp32c6))]
136    {
137        Esp32Variant::Esp32C6
138    }
139
140    #[cfg(all(target_arch = "riscv32", esp32h2))]
141    {
142        Esp32Variant::Esp32H2
143    }
144
145    #[cfg(not(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2)))]
146    {
147        Esp32Variant::Esp32 // Default fallback
148    }
149}
150
151/// ESP32 memory barrier
152#[inline]
153pub fn memory_barrier() {
154    fence(Ordering::SeqCst);
155
156    #[cfg(target_arch = "xtensa")]
157    {
158        unsafe {
159            // MEMW instruction for Xtensa
160            core::arch::asm!("memw", options(nostack, nomem));
161        }
162    }
163
164    #[cfg(all(target_arch = "riscv32", any(esp32c3, esp32c6, esp32h2)))]
165    {
166        unsafe {
167            core::arch::asm!("fence rw, rw", options(nostack, nomem));
168        }
169    }
170}
171
172/// Get cycle count from ESP32
173#[inline]
174pub fn cycle_count(variant: Esp32Variant) -> Option<u64> {
175    match variant {
176        Esp32Variant::Esp32 | Esp32Variant::Esp32S2 | Esp32Variant::Esp32S3 => {
177            #[cfg(target_arch = "xtensa")]
178            {
179                let count: u32;
180                unsafe {
181                    // CCOUNT special register
182                    core::arch::asm!(
183                        "rsr.ccount {0}",
184                        out(reg) count,
185                        options(nostack, nomem, preserves_flags)
186                    );
187                }
188                Some(count as u64)
189            }
190
191            #[cfg(not(target_arch = "xtensa"))]
192            {
193                None
194            }
195        }
196        Esp32Variant::Esp32C3 | Esp32Variant::Esp32C6 | Esp32Variant::Esp32H2 => {
197            // Use RISC-V cycle counter
198            #[cfg(target_arch = "riscv32")]
199            {
200                let low: u32;
201                let high1: u32;
202                let high2: u32;
203
204                unsafe {
205                    loop {
206                        core::arch::asm!(
207                            "rdcycleh {high}",
208                            "rdcycle {low}",
209                            "rdcycleh {high2}",
210                            high = out(reg) high1,
211                            low = out(reg) low,
212                            high2 = out(reg) high2,
213                            options(nostack, nomem, preserves_flags)
214                        );
215
216                        if high1 == high2 {
217                            break;
218                        }
219                    }
220                }
221
222                Some(((high1 as u64) << 32) | (low as u64))
223            }
224
225            #[cfg(not(target_arch = "riscv32"))]
226            {
227                None
228            }
229        }
230    }
231}
232
233/// ESP32 WiFi and wireless support
234pub mod wireless {
235    use crate::error::Result;
236
237    /// WiFi power mode
238    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
239    pub enum WifiPowerMode {
240        /// Full power
241        Active,
242        /// Modem sleep
243        ModemSleep,
244        /// Light sleep
245        LightSleep,
246    }
247
248    /// Set WiFi power mode (placeholder for actual implementation)
249    pub fn set_wifi_power_mode(_mode: WifiPowerMode) -> Result<()> {
250        // This would interface with ESP-IDF or esp-hal
251        Ok(())
252    }
253}
254
255/// ESP32 RTC and sleep modes
256pub mod sleep {
257    use crate::error::Result;
258
259    /// Deep sleep wakeup source
260    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
261    pub enum WakeupSource {
262        /// Timer wakeup
263        Timer,
264        /// GPIO wakeup
265        Gpio,
266        /// Touch pad wakeup
267        Touchpad,
268        /// ULP coprocessor wakeup
269        Ulp,
270    }
271
272    /// Enter deep sleep mode
273    pub fn deep_sleep(_duration_us: u64, _sources: &[WakeupSource]) -> Result<()> {
274        // This would interface with ESP-IDF or esp-hal
275        Ok(())
276    }
277
278    /// Enter light sleep mode
279    pub fn light_sleep(_duration_us: u64) -> Result<()> {
280        // This would interface with ESP-IDF or esp-hal
281        Ok(())
282    }
283}
284
285/// ESP32 flash operations
286pub mod flash {
287    use crate::error::{EmbeddedError, Result};
288
289    /// Read from flash memory
290    ///
291    /// # Safety
292    ///
293    /// addr must be a valid flash address
294    pub unsafe fn read(_addr: u32, _buffer: &mut [u8]) -> Result<usize> {
295        // This would interface with ESP flash driver
296        Err(EmbeddedError::UnsupportedOperation)
297    }
298
299    /// Write to flash memory
300    ///
301    /// # Safety
302    ///
303    /// addr must be a valid flash address and sector must be erased
304    pub unsafe fn write(_addr: u32, _data: &[u8]) -> Result<usize> {
305        // This would interface with ESP flash driver
306        Err(EmbeddedError::UnsupportedOperation)
307    }
308
309    /// Erase flash sector
310    ///
311    /// # Safety
312    ///
313    /// addr must be a valid flash address aligned to sector boundary
314    pub unsafe fn erase_sector(_addr: u32) -> Result<()> {
315        // This would interface with ESP flash driver
316        Err(EmbeddedError::UnsupportedOperation)
317    }
318}
319
320/// ESP32 hardware crypto acceleration
321#[cfg(any(esp32, esp32s3, esp32c6))]
322pub mod crypto {
323    use crate::error::{EmbeddedError, Result};
324
325    /// AES encryption mode
326    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
327    pub enum AesMode {
328        /// ECB mode
329        Ecb,
330        /// CBC mode
331        Cbc,
332        /// CTR mode
333        Ctr,
334    }
335
336    /// AES key size
337    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
338    pub enum AesKeySize {
339        /// 128-bit key
340        Aes128,
341        /// 192-bit key
342        Aes192,
343        /// 256-bit key
344        Aes256,
345    }
346
347    /// Encrypt data using hardware AES
348    pub fn aes_encrypt(
349        _mode: AesMode,
350        _key_size: AesKeySize,
351        _key: &[u8],
352        _iv: Option<&[u8]>,
353        _input: &[u8],
354        _output: &mut [u8],
355    ) -> Result<()> {
356        // This would interface with ESP-IDF crypto driver
357        Err(EmbeddedError::UnsupportedOperation)
358    }
359
360    /// Decrypt data using hardware AES
361    pub fn aes_decrypt(
362        _mode: AesMode,
363        _key_size: AesKeySize,
364        _key: &[u8],
365        _iv: Option<&[u8]>,
366        _input: &[u8],
367        _output: &mut [u8],
368    ) -> Result<()> {
369        // This would interface with ESP-IDF crypto driver
370        Err(EmbeddedError::UnsupportedOperation)
371    }
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377
378    #[test]
379    fn test_esp32_target() {
380        let target = Esp32Target::new(Esp32Variant::Esp32);
381        assert_eq!(target.name(), "ESP32");
382        assert_eq!(target.pointer_size(), 4);
383        assert_eq!(target.native_alignment(), 4);
384    }
385
386    #[test]
387    fn test_variant_detection() {
388        let variant = detect_variant();
389        // Should compile without panic
390        let _ = variant;
391    }
392
393    #[test]
394    fn test_capabilities() {
395        let caps = get_capabilities();
396        assert!(caps.cache_line_size > 0);
397        assert!(caps.num_cores > 0);
398    }
399}