use super::{TargetArch, TargetCapabilities};
use core::sync::atomic::{Ordering, fence};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Esp32Variant {
Esp32,
Esp32S2,
Esp32S3,
Esp32C3,
Esp32C6,
Esp32H2,
}
pub struct Esp32Target {
variant: Esp32Variant,
}
impl Esp32Target {
pub const fn new(variant: Esp32Variant) -> Self {
Self { variant }
}
pub const fn variant(&self) -> Esp32Variant {
self.variant
}
pub const fn is_xtensa(&self) -> bool {
matches!(
self.variant,
Esp32Variant::Esp32 | Esp32Variant::Esp32S2 | Esp32Variant::Esp32S3
)
}
pub const fn is_riscv(&self) -> bool {
matches!(
self.variant,
Esp32Variant::Esp32C3 | Esp32Variant::Esp32C6 | Esp32Variant::Esp32H2
)
}
}
impl TargetArch for Esp32Target {
fn name(&self) -> &'static str {
match self.variant {
Esp32Variant::Esp32 => "ESP32",
Esp32Variant::Esp32S2 => "ESP32-S2",
Esp32Variant::Esp32S3 => "ESP32-S3",
Esp32Variant::Esp32C3 => "ESP32-C3",
Esp32Variant::Esp32C6 => "ESP32-C6",
Esp32Variant::Esp32H2 => "ESP32-H2",
}
}
fn pointer_size(&self) -> usize {
4 }
fn native_alignment(&self) -> usize {
4 }
fn supports_unaligned_access(&self) -> bool {
self.is_xtensa()
}
fn memory_barrier(&self) {
memory_barrier();
}
fn cycle_count(&self) -> Option<u64> {
cycle_count(self.variant)
}
}
pub fn get_capabilities() -> TargetCapabilities {
let variant = detect_variant();
TargetCapabilities {
has_fpu: true, has_simd: false,
has_aes: matches!(
variant,
Esp32Variant::Esp32 | Esp32Variant::Esp32S3 | Esp32Variant::Esp32C6
),
has_crc: false,
cache_line_size: 32, num_cores: match variant {
Esp32Variant::Esp32 | Esp32Variant::Esp32S3 => 2,
_ => 1,
},
}
}
pub const fn detect_variant() -> Esp32Variant {
#[cfg(all(target_arch = "xtensa", esp32))]
{
Esp32Variant::Esp32
}
#[cfg(all(target_arch = "xtensa", esp32s2))]
{
Esp32Variant::Esp32S2
}
#[cfg(all(target_arch = "xtensa", esp32s3))]
{
Esp32Variant::Esp32S3
}
#[cfg(all(target_arch = "riscv32", esp32c3))]
{
Esp32Variant::Esp32C3
}
#[cfg(all(target_arch = "riscv32", esp32c6))]
{
Esp32Variant::Esp32C6
}
#[cfg(all(target_arch = "riscv32", esp32h2))]
{
Esp32Variant::Esp32H2
}
#[cfg(not(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2)))]
{
Esp32Variant::Esp32 }
}
#[inline]
pub fn memory_barrier() {
fence(Ordering::SeqCst);
#[cfg(target_arch = "xtensa")]
{
unsafe {
core::arch::asm!("memw", options(nostack, nomem));
}
}
#[cfg(all(target_arch = "riscv32", any(esp32c3, esp32c6, esp32h2)))]
{
unsafe {
core::arch::asm!("fence rw, rw", options(nostack, nomem));
}
}
}
#[inline]
pub fn cycle_count(variant: Esp32Variant) -> Option<u64> {
match variant {
Esp32Variant::Esp32 | Esp32Variant::Esp32S2 | Esp32Variant::Esp32S3 => {
#[cfg(target_arch = "xtensa")]
{
let count: u32;
unsafe {
core::arch::asm!(
"rsr.ccount {0}",
out(reg) count,
options(nostack, nomem, preserves_flags)
);
}
Some(count as u64)
}
#[cfg(not(target_arch = "xtensa"))]
{
None
}
}
Esp32Variant::Esp32C3 | Esp32Variant::Esp32C6 | Esp32Variant::Esp32H2 => {
#[cfg(target_arch = "riscv32")]
{
let low: u32;
let high1: u32;
let high2: u32;
unsafe {
loop {
core::arch::asm!(
"rdcycleh {high}",
"rdcycle {low}",
"rdcycleh {high2}",
high = out(reg) high1,
low = out(reg) low,
high2 = out(reg) high2,
options(nostack, nomem, preserves_flags)
);
if high1 == high2 {
break;
}
}
}
Some(((high1 as u64) << 32) | (low as u64))
}
#[cfg(not(target_arch = "riscv32"))]
{
None
}
}
}
}
pub mod wireless {
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WifiPowerMode {
Active,
ModemSleep,
LightSleep,
}
pub fn set_wifi_power_mode(_mode: WifiPowerMode) -> Result<()> {
Ok(())
}
}
pub mod sleep {
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WakeupSource {
Timer,
Gpio,
Touchpad,
Ulp,
}
pub fn deep_sleep(_duration_us: u64, _sources: &[WakeupSource]) -> Result<()> {
Ok(())
}
pub fn light_sleep(_duration_us: u64) -> Result<()> {
Ok(())
}
}
pub mod flash {
use crate::error::{EmbeddedError, Result};
pub unsafe fn read(_addr: u32, _buffer: &mut [u8]) -> Result<usize> {
Err(EmbeddedError::UnsupportedOperation)
}
pub unsafe fn write(_addr: u32, _data: &[u8]) -> Result<usize> {
Err(EmbeddedError::UnsupportedOperation)
}
pub unsafe fn erase_sector(_addr: u32) -> Result<()> {
Err(EmbeddedError::UnsupportedOperation)
}
}
#[cfg(any(esp32, esp32s3, esp32c6))]
pub mod crypto {
use crate::error::{EmbeddedError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AesMode {
Ecb,
Cbc,
Ctr,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AesKeySize {
Aes128,
Aes192,
Aes256,
}
pub fn aes_encrypt(
_mode: AesMode,
_key_size: AesKeySize,
_key: &[u8],
_iv: Option<&[u8]>,
_input: &[u8],
_output: &mut [u8],
) -> Result<()> {
Err(EmbeddedError::UnsupportedOperation)
}
pub fn aes_decrypt(
_mode: AesMode,
_key_size: AesKeySize,
_key: &[u8],
_iv: Option<&[u8]>,
_input: &[u8],
_output: &mut [u8],
) -> Result<()> {
Err(EmbeddedError::UnsupportedOperation)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_esp32_target() {
let target = Esp32Target::new(Esp32Variant::Esp32);
assert_eq!(target.name(), "ESP32");
assert_eq!(target.pointer_size(), 4);
assert_eq!(target.native_alignment(), 4);
}
#[test]
fn test_variant_detection() {
let variant = detect_variant();
let _ = variant;
}
#[test]
fn test_capabilities() {
let caps = get_capabilities();
assert!(caps.cache_line_size > 0);
assert!(caps.num_cores > 0);
}
}