diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 201b2e3cfb0..9827fbc931a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,16 +108,49 @@ jobs: # Subsequent steps can just check the examples instead, as we're already # confident that they link. - name: check esp32c3-hal (common features) - run: cargo check --manifest-path=esp32c3-hal/Cargo.toml --target=riscv32imc-unknown-none-elf --examples --features=eh1,smartled,ufmt + run: cd esp32c3-hal/ && cargo check --examples --features=eh1,smartled,ufmt - name: check esp32c3-hal (async, systick) - run: cargo check --manifest-path=esp32c3-hal/Cargo.toml --target=riscv32imc-unknown-none-elf --example=embassy_hello_world --features=embassy,embassy-time-systick + run: cd esp32c3-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-systick - name: check esp32c3-hal (async, timg0) - run: cargo check --manifest-path=esp32c3-hal/Cargo.toml --target=riscv32imc-unknown-none-elf --example=embassy_hello_world --features=embassy,embassy-time-timg0 + run: cd esp32c3-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0 - name: check esp32c3-hal (async, gpio) run: cd esp32c3-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-systick,async - name: check esp32c3-hal (async, spi) run: cd esp32c3-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-systick,async + esp32c6-hal: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@v1 + with: + target: riscv32imac-unknown-none-elf + toolchain: nightly + components: rust-src + - uses: Swatinem/rust-cache@v2 + + # Perform a full build initially to verify that the examples not only + # build, but also link successfully. + # We also use this as an opportunity to verify that the examples link + # for each supported image format. + - name: build esp32c6-hal (no features) + run: cd esp32c6-hal/ && cargo build --examples + - name: build esp32c6-hal (direct-boot) + run: cd esp32c6-hal/ && cargo build --examples --features=direct-boot + # Subsequent steps can just check the examples instead, as we're already + # confident that they link. + - name: check esp32c6-hal (common features) + run: cd esp32c6-hal/ && cargo check --examples --features=eh1,smartled,ufmt + - name: check esp32c6-hal (async, systick) + run: cd esp32c6-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-systick + - name: check esp32c6-hal (async, timg0) + run: cd esp32c6-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0 + - name: check esp32c6-hal (async, gpio) + run: cd esp32c6-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-systick,async + - name: check esp32c6-hal (async, spi) + run: cd esp32c6-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-systick,async + esp32s2-hal: runs-on: ubuntu-latest @@ -190,7 +223,7 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 with: - target: riscv32imc-unknown-none-elf + target: riscv32imc-unknown-none-elf, riscv32imac-unknown-none-elf toolchain: "1.65.0" - uses: Swatinem/rust-cache@v2 @@ -199,6 +232,8 @@ jobs: run: cd esp32c2-hal/ && cargo check --features=eh1,ufmt - name: msrv (esp32c3-hal) run: cd esp32c3-hal/ && cargo check --features=eh1,ufmt,smartled + - name: msrv (esp32c6-hal) + run: cd esp32c6-hal/ && cargo check --features=eh1,ufmt,smartled msrv-xtensa: runs-on: ubuntu-latest @@ -238,6 +273,8 @@ jobs: run: cargo +stable clippy --manifest-path=esp32c2-hal/Cargo.toml -- --no-deps - name: clippy (esp32c3-hal) run: cargo +stable clippy --manifest-path=esp32c3-hal/Cargo.toml -- --no-deps + - name: clippy (esp32c6-hal) + run: cargo +stable clippy --manifest-path=esp32c6-hal/Cargo.toml -- --no-deps clippy-xtensa: runs-on: ubuntu-latest @@ -286,7 +323,9 @@ jobs: run: cargo fmt --all --manifest-path=esp32c2-hal/Cargo.toml -- --check - name: rustfmt (esp32c3-hal) run: cargo fmt --all --manifest-path=esp32c3-hal/Cargo.toml -- --check + - name: rustfmt (esp32c6-hal) + run: cargo fmt --all --manifest-path=esp32c6-hal/Cargo.toml -- --check - name: rustfmt (esp32s2-hal) run: cargo fmt --all --manifest-path=esp32s2-hal/Cargo.toml -- --check - name: rustfmt (esp32s3-hal) - run: cargo fmt --all --manifest-path=esp32s3-hal/Cargo.toml -- --check + run: cargo fmt --all --manifest-path=esp32s3-hal/Cargo.toml -- --check \ No newline at end of file diff --git a/README.md b/README.md index 35e42820cd7..189350e7111 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![MIT/Apache-2.0 licensed](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue?style=flat-square) [![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org) -**H**ardware **A**bstraction **L**ayer crates for the **ESP32**, **ESP32-C2**, **ESP32-C3**, **ESP32-S2**, and **ESP32-S3** from Espressif. +**H**ardware **A**bstraction **L**ayer crates for the **ESP32**, **ESP32-C2/C3/C6**, and **ESP32-S2/S3** from Espressif. These HALs are `no_std`; if you are looking for `std` support, please use [esp-idf-hal] instead. @@ -12,13 +12,14 @@ This project is still in the early stages of development, and as such there shou If you have any questions, comments, or concerns, please [open an issue], [start a new discussion], or join us on [Matrix]. For additional information regarding any of the crates in this repository, please refer to the crate's README. -| Crate | Target | Technical Reference Manual | -| :-----------: | :-----------------------------------------------------------------: | :------------------------: | -| [esp32-hal] | `xtensa-esp32-none-elf` | [ESP32] | -| [esp32c2-hal] | `riscv32imc-unknown-none-elf` | [ESP32-C2] | -| [esp32c3-hal] | `riscv32imc-unknown-none-elf` | [ESP32-C3] | -| [esp32s2-hal] | `xtensa-esp32s2-none-elf` | [ESP32-S2] | -| [esp32s3-hal] | `xtensa-esp32s3-none-elf` | [ESP32-S3] | +| Crate | Target | Technical Reference Manual | +| :-----------: | :----------------------------: | :------------------------: | +| [esp32-hal] | `xtensa-esp32-none-elf` | [ESP32] | +| [esp32c2-hal] | `riscv32imc-unknown-none-elf` | [ESP32-C2] | +| [esp32c3-hal] | `riscv32imc-unknown-none-elf` | [ESP32-C3] | +| [esp32c6-hal] | `riscv32imac-unknown-none-elf` | [ESP32-C6] | +| [esp32s2-hal] | `xtensa-esp32s2-none-elf` | [ESP32-S2] | +| [esp32s3-hal] | `xtensa-esp32s3-none-elf` | [ESP32-S3] | [here]: https://github.com/esp-rs/esp-hal/issues/19 [esp-idf-hal]: https://github.com/esp-rs/esp-idf-hal @@ -28,11 +29,13 @@ If you have any questions, comments, or concerns, please [open an issue], [start [esp32-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32-hal [esp32c2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c2-hal [esp32c3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c3-hal +[esp32c6-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c6-hal [esp32s2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s2-hal [esp32s3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s3-hal [esp32]: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf [esp32-c2]: https://www.espressif.com/sites/default/files/documentation/esp8684_technical_reference_manual_en.pdf [esp32-c3]: https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf +[esp32-c6]: https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf [esp32-s2]: https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf [esp32-s3]: https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf [atomic emulation]: https://github.com/esp-rs/riscv-atomic-emulation-trap @@ -62,6 +65,7 @@ There are a number of other crates within the [esp-rs organization] which can be | [esp-backtrace] | Backtrace support for bare-metal applications | | [esp-println] | Provides `print!` and `println!` implementations | | [esp-storage] | Implementation of [embedded-storage] traits to access unencrypted flash memory | +| [esp-wifi] | `no_std` Wi-Fi/Bluetooth LE support | [esp-rs organization]: https://github.com/esp-rs [esp-alloc]: https://github.com/esp-rs/esp-alloc @@ -69,12 +73,13 @@ There are a number of other crates within the [esp-rs organization] which can be [esp-println]: https://github.com/esp-rs/esp-println [esp-storage]: https://github.com/esp-rs/esp-storage [embedded-storage]: https://github.com/rust-embedded-community/embedded-storage +[esp-wifi]: https://github.com/esp-rs/esp-wifi ## MSRV The **M**inimum **S**upported **R**ust **V**ersions are: -- `1.65.0` for RISC-V devices (**ESP32-C2**, **ESP32-C3**) +- `1.65.0` for RISC-V devices (**ESP32-C2**, **ESP32-C3**, **ESP32-C6**) - `1.65.0` for Xtensa devices (**ESP32**, **ESP32-S2**, **ESP32-S3**) - `1.67.0` for all `async` examples (`embassy_hello_world`, `embassy_wait`, etc.) diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 7aaca8ad0b5..98ce16eff8e 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -51,18 +51,19 @@ ufmt-write = { version = "0.1.0", optional = true } # IMPORTANT: # Each supported device MUST have its PAC included below along with a -# corresponding feature. We rename the PAC packages because we cannot -# have dependencies and features with the same names. +# corresponding feature. esp32 = { version = "0.21.0", features = ["critical-section"], optional = true } esp32c2 = { version = "0.8.0", features = ["critical-section"], optional = true } esp32c3 = { version = "0.11.0", features = ["critical-section"], optional = true } +esp32c6 = { version = "0.1.0", features = ["critical-section"], optional = true } esp32s2 = { version = "0.12.0", features = ["critical-section"], optional = true } esp32s3 = { version = "0.15.0", features = ["critical-section"], optional = true } [features] esp32 = ["esp32/rt" , "xtensa", "xtensa-lx/esp32", "xtensa-lx-rt/esp32", "lock_api", "procmacros/esp32"] -esp32c2 = ["esp32c2/rt", "riscv", "procmacros/esp32c2"] -esp32c3 = ["esp32c3/rt", "riscv", "procmacros/esp32c3"] +esp32c2 = ["esp32c2/rt", "riscv", "procmacros/esp32c2"] +esp32c3 = ["esp32c3/rt", "riscv", "procmacros/esp32c3"] +esp32c6 = ["esp32c6/rt", "riscv", "procmacros/esp32c6"] esp32s2 = ["esp32s2/rt", "xtensa", "xtensa-lx/esp32s2", "xtensa-lx-rt/esp32s2", "esp-synopsys-usb-otg", "usb-device", "procmacros/esp32s2"] esp32s3 = ["esp32s3/rt", "xtensa", "xtensa-lx/esp32s3", "xtensa-lx-rt/esp32s3", "lock_api", "esp-synopsys-usb-otg", "usb-device", "procmacros/esp32s3"] diff --git a/esp-hal-common/README.md b/esp-hal-common/README.md index 98fbe61d921..63d364a08a7 100644 --- a/esp-hal-common/README.md +++ b/esp-hal-common/README.md @@ -12,6 +12,7 @@ This crate should not be used directly; you should use one of the device-specifi - [esp32-hal](../esp32-hal/README.md) - [esp32c2-hal](../esp32c2-hal/README.md) - [esp32c3-hal](../esp32c3-hal/README.md) +- [esp32c6-hal](../esp32c6-hal/README.md) - [esp32s2-hal](../esp32s2-hal/README.md) - [esp32s3-hal](../esp32s3-hal/README.md) diff --git a/esp-hal-common/build.rs b/esp-hal-common/build.rs index e57f9ed15e5..b3e7ccd85ed 100644 --- a/esp-hal-common/build.rs +++ b/esp-hal-common/build.rs @@ -2,11 +2,12 @@ fn main() { let esp32 = cfg!(feature = "esp32"); let esp32c2 = cfg!(feature = "esp32c2"); let esp32c3 = cfg!(feature = "esp32c3"); + let esp32c6 = cfg!(feature = "esp32c6"); let esp32s2 = cfg!(feature = "esp32s2"); let esp32s3 = cfg!(feature = "esp32s3"); // Ensure that exactly one chip has been specified - let chip_features = [esp32, esp32c2, esp32c3, esp32s2, esp32s3]; + let chip_features = [esp32, esp32c2, esp32c3, esp32c6, esp32s2, esp32s3]; match chip_features.iter().filter(|&&f| f).count() { 1 => {} n => panic!("Exactly 1 chip must be enabled via its Cargo feature, {n} provided"), @@ -46,6 +47,7 @@ fn main() { // - 'usb_otg' // - 'usb_serial_jtag' // - 'aes' + // - 'plic' // - 'radio' // // New symbols can be added as needed, but please be sure to update both this @@ -94,6 +96,23 @@ fn main() { "aes", "radio", ] + } else if esp32c6 { + vec![ + "esp32c6", + "riscv", + "single_core", + "gdma", + "i2s", + "mcpwm", + "rmt", + "systimer", + "timg0", + "timg1", + "usb_serial_jtag", + "plic", + "aes", + "radio", + ] } else if esp32s2 { vec![ "esp32s2", diff --git a/esp-hal-common/src/aes/esp32c3.rs b/esp-hal-common/src/aes/esp32cX.rs similarity index 100% rename from esp-hal-common/src/aes/esp32c3.rs rename to esp-hal-common/src/aes/esp32cX.rs diff --git a/esp-hal-common/src/aes/mod.rs b/esp-hal-common/src/aes/mod.rs index 441915342a0..bb1d9e216f1 100644 --- a/esp-hal-common/src/aes/mod.rs +++ b/esp-hal-common/src/aes/mod.rs @@ -30,7 +30,8 @@ use crate::{ #[cfg_attr(esp32, path = "esp32.rs")] #[cfg_attr(esp32s3, path = "esp32s3.rs")] #[cfg_attr(esp32s2, path = "esp32s2.rs")] -#[cfg_attr(esp32c3, path = "esp32c3.rs")] +#[cfg_attr(esp32c3, path = "esp32cX.rs")] +#[cfg_attr(esp32c6, path = "esp32cX.rs")] mod aes_spec_impl; const ALIGN_SIZE: usize = core::mem::size_of::(); diff --git a/esp-hal-common/src/analog/adc/riscv.rs b/esp-hal-common/src/analog/adc/riscv.rs index 20333238dca..f0c025c9504 100644 --- a/esp-hal-common/src/analog/adc/riscv.rs +++ b/esp-hal-common/src/analog/adc/riscv.rs @@ -287,6 +287,30 @@ macro_rules! impl_adc_interface { pub use impl_adc_interface; +#[cfg(esp32c2)] +pub mod implementation { + //! Analog to digital (ADC) conversion support. + //! + //! This module provides functions for reading analog values from the + //! analog to digital converter available on the ESP32-C2: `ADC1`. + + use embedded_hal::adc::Channel; + + use super::impl_adc_interface; + pub use crate::analog::{adc::*, ADC1}; + use crate::gpio::*; + + impl_adc_interface! { + ADC1 [ + (Gpio0, 0), + (Gpio1, 1), + (Gpio2, 2), + (Gpio3, 3), + (Gpio4, 4), + ] + } +} + #[cfg(esp32c3)] pub mod implementation { //! Analog to digital (ADC) conversion support. @@ -318,12 +342,12 @@ pub mod implementation { } } -#[cfg(esp32c2)] +#[cfg(esp32c6)] pub mod implementation { //! Analog to digital (ADC) conversion support. //! - //! This module provides functions for reading analog values from the - //! analog to digital converter available on the ESP32-C2: `ADC1`. + //! This module provides functions for reading analog values from one + //! analog to digital converter available on the ESP32-C6: `ADC1`. use embedded_hal::adc::Channel; @@ -338,6 +362,8 @@ pub mod implementation { (Gpio2, 2), (Gpio3, 3), (Gpio4, 4), + (Gpio5, 5), + (Gpio6, 6), ] } } diff --git a/esp-hal-common/src/analog/mod.rs b/esp-hal-common/src/analog/mod.rs index 316d8ce2d13..cd4aa748909 100644 --- a/esp-hal-common/src/analog/mod.rs +++ b/esp-hal-common/src/analog/mod.rs @@ -1,8 +1,6 @@ #[cfg_attr(esp32, path = "adc/esp32.rs")] -#[cfg_attr(esp32c2, path = "adc/riscv.rs")] -#[cfg_attr(esp32c3, path = "adc/riscv.rs")] -#[cfg_attr(esp32s2, path = "adc/xtensa.rs")] -#[cfg_attr(esp32s3, path = "adc/xtensa.rs")] +#[cfg_attr(any(esp32c2, esp32c3, esp32c6), path = "adc/riscv.rs")] +#[cfg_attr(any(esp32s2, esp32s3), path = "adc/xtensa.rs")] pub mod adc; #[cfg(dac)] pub mod dac; @@ -10,6 +8,7 @@ pub mod dac; pub struct ADC1 { _private: (), } + pub struct ADC2 { _private: (), } @@ -95,6 +94,7 @@ impl crate::peripheral::sealed::Sealed for DAC1 {} impl crate::peripheral::Peripheral for DAC2 { type P = DAC2; + #[inline] unsafe fn clone_unchecked(&mut self) -> Self::P { DAC2 { _private: () } @@ -103,6 +103,7 @@ impl crate::peripheral::Peripheral for DAC2 { impl crate::peripheral::Peripheral for &mut DAC2 { type P = DAC2; + #[inline] unsafe fn clone_unchecked(&mut self) -> Self::P { DAC2 { _private: () } @@ -114,7 +115,6 @@ impl crate::peripheral::sealed::Sealed for DAC2 {} cfg_if::cfg_if! { if #[cfg(any(esp32, esp32s2, esp32s3))] { - use crate::peripherals::SENS; pub struct AvailableAnalog { @@ -151,12 +151,12 @@ cfg_if::cfg_if! { } cfg_if::cfg_if! { - if #[cfg(esp32c3)] { - + if #[cfg(any(esp32c2, esp32c3, esp32c6))] { use crate::peripherals::APB_SARADC; pub struct AvailableAnalog { pub adc1: ADC1, + #[cfg(esp32c3)] pub adc2: ADC2, } @@ -171,6 +171,7 @@ cfg_if::cfg_if! { adc1: ADC1 { _private: (), }, + #[cfg(esp32c3)] adc2: ADC2 { _private: (), }, @@ -179,29 +180,3 @@ cfg_if::cfg_if! { } } } - -cfg_if::cfg_if! { - if #[cfg(esp32c2)] { - - use crate::peripherals::APB_SARADC; - - pub struct AvailableAnalog { - pub adc1: ADC1, - } - - /// Extension trait to split a APB_SARADC peripheral in independent parts - pub trait SarAdcExt { - fn split(self) -> AvailableAnalog; - } - - impl<'d, T: crate::peripheral::Peripheral

+ 'd> SarAdcExt for T { - fn split(self) -> AvailableAnalog { - AvailableAnalog { - adc1: ADC1 { - _private: (), - }, - } - } - } - } -} diff --git a/esp-hal-common/src/clock/clocks_ll/esp32c6.rs b/esp-hal-common/src/clock/clocks_ll/esp32c6.rs new file mode 100644 index 00000000000..80f9886068e --- /dev/null +++ b/esp-hal-common/src/clock/clocks_ll/esp32c6.rs @@ -0,0 +1,369 @@ +use crate::clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock}; + +extern "C" { + fn ets_update_cpu_frequency(ticks_per_us: u32); +} + +const I2C_BBPLL: u8 = 0x66; +const I2C_BBPLL_HOSTID: u8 = 0; + +const I2C_BBPLL_OC_REF_DIV: u8 = 2; +const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4; + +const I2C_BBPLL_OC_DIV_7_0: u8 = 3; + +const I2C_BBPLL_OC_DR1: u8 = 5; +const I2C_BBPLL_OC_DR1_MSB: u8 = 2; +const I2C_BBPLL_OC_DR1_LSB: u8 = 0; + +const I2C_BBPLL_OC_DR3: u8 = 5; +const I2C_BBPLL_OC_DR3_MSB: u8 = 6; +const I2C_BBPLL_OC_DR3_LSB: u8 = 4; + +const I2C_BBPLL_OC_DCUR: u8 = 6; + +const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4; + +const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6; + +const I2C_BBPLL_OC_VCO_DBIAS: u8 = 9; +const I2C_BBPLL_OC_VCO_DBIAS_MSB: u8 = 1; +const I2C_BBPLL_OC_VCO_DBIAS_LSB: u8 = 0; + +// Analog function control register +const I2C_MST_ANA_CONF0_REG: u32 = 0x600AF818; +const I2C_MST_BBPLL_STOP_FORCE_HIGH: u32 = 1 << 2; +const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 3; +const I2C_MST_BBPLL_CAL_DONE: u32 = 1 << 24; + +const MODEM_LPCON_CLK_CONF_FORCE_ON_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x1c; +const MODEM_LPCON_CLK_I2C_MST_FO: u32 = 1 << 2; +const MODEM_LPCON_I2C_MST_CLK_CONF_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x10; +const MODEM_LPCON_CLK_I2C_MST_SEL_160M: u32 = 1 << 0; + +pub(crate) fn esp32c6_rtc_bbpll_configure(_xtal_freq: XtalClock, _pll_freq: PllClock) { + unsafe { + // enable i2c mst clk by force on temporarily + (MODEM_LPCON_CLK_CONF_FORCE_ON_REG as *mut u32).write_volatile( + (MODEM_LPCON_CLK_CONF_FORCE_ON_REG as *mut u32).read_volatile() + | MODEM_LPCON_CLK_I2C_MST_FO, + ); + (MODEM_LPCON_I2C_MST_CLK_CONF_REG as *mut u32).write_volatile( + (MODEM_LPCON_I2C_MST_CLK_CONF_REG as *mut u32).read_volatile() + | MODEM_LPCON_CLK_I2C_MST_SEL_160M, + ); + + let i2c_mst_ana_conf0_reg_ptr = I2C_MST_ANA_CONF0_REG as *mut u32; + // BBPLL CALIBRATION START + i2c_mst_ana_conf0_reg_ptr.write_volatile( + i2c_mst_ana_conf0_reg_ptr.read_volatile() & !I2C_MST_BBPLL_STOP_FORCE_HIGH, + ); + i2c_mst_ana_conf0_reg_ptr.write_volatile( + i2c_mst_ana_conf0_reg_ptr.read_volatile() | I2C_MST_BBPLL_STOP_FORCE_LOW, + ); + + let div_ref = 0u32; + let div7_0 = 8u32; + let dr1 = 0u32; + let dr3 = 0u32; + let dchgp = 5u32; + let dcur = 3u32; + let dbias = 2u32; + + let i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref; + let i2c_bbpll_div_7_0 = div7_0; + let i2c_bbpll_dcur = + (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; + + regi2c_write( + I2C_BBPLL, + I2C_BBPLL_HOSTID, + I2C_BBPLL_OC_REF_DIV, + i2c_bbpll_lref as u8, + ); + regi2c_write( + I2C_BBPLL, + I2C_BBPLL_HOSTID, + I2C_BBPLL_OC_DIV_7_0, + i2c_bbpll_div_7_0 as u8, + ); + regi2c_write_mask( + I2C_BBPLL, + I2C_BBPLL_HOSTID, + I2C_BBPLL_OC_DR1, + I2C_BBPLL_OC_DR1_MSB, + I2C_BBPLL_OC_DR1_LSB, + dr1 as u8, + ); + regi2c_write_mask( + I2C_BBPLL, + I2C_BBPLL_HOSTID, + I2C_BBPLL_OC_DR3, + I2C_BBPLL_OC_DR3_MSB, + I2C_BBPLL_OC_DR3_LSB, + dr3 as u8, + ); + regi2c_write( + I2C_BBPLL, + I2C_BBPLL_HOSTID, + I2C_BBPLL_OC_DCUR, + i2c_bbpll_dcur as u8, + ); + regi2c_write_mask( + I2C_BBPLL, + I2C_BBPLL_HOSTID, + I2C_BBPLL_OC_VCO_DBIAS, + I2C_BBPLL_OC_VCO_DBIAS_MSB, + I2C_BBPLL_OC_VCO_DBIAS_LSB, + dbias as u8, + ); + + // WAIT CALIBRATION DONE + while (i2c_mst_ana_conf0_reg_ptr.read_volatile() & I2C_MST_BBPLL_CAL_DONE) == 0 {} + + // BBPLL CALIBRATION STOP + i2c_mst_ana_conf0_reg_ptr.write_volatile( + i2c_mst_ana_conf0_reg_ptr.read_volatile() | I2C_MST_BBPLL_STOP_FORCE_HIGH, + ); + i2c_mst_ana_conf0_reg_ptr.write_volatile( + i2c_mst_ana_conf0_reg_ptr.read_volatile() & !I2C_MST_BBPLL_STOP_FORCE_LOW, + ); + } +} + +pub(crate) fn esp32c6_rtc_bbpll_enable() { + let pmu = unsafe { &*crate::peripherals::PMU::PTR }; + + pmu.imm_hp_ck_power.modify(|_, w| { + w.tie_high_xpd_bb_i2c() + .set_bit() + .tie_high_xpd_bbpll() + .set_bit() + .tie_high_xpd_bbpll_i2c() + .set_bit() + }); + + pmu.imm_hp_ck_power + .modify(|_, w| w.tie_high_global_bbpll_icg().set_bit()); +} + +pub(crate) fn esp32c6_rtc_update_to_xtal(freq: XtalClock, _div: u8) { + let pcr = unsafe { &*crate::peripherals::PCR::PTR }; + unsafe { + ets_update_cpu_frequency(freq.mhz()); + // Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) + // first. + pcr.apb_freq_conf + .modify(|_, w| w.apb_div_num().bits(0).apb_div_num().bits(_div - 1)); + + // Switch clock source + pcr.sysclk_conf.modify(|_, w| w.soc_clk_sel().bits(0)); + } +} + +pub(crate) fn esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) { + // On ESP32C6, MSPI source clock's default HS divider leads to 120MHz, which is + // unusable before calibration Therefore, before switching SOC_ROOT_CLK to + // HS, we need to set MSPI source clock HS divider to make it run at + // 80MHz after the switch. PLL = 480MHz, so divider is 6. + clk_ll_mspi_fast_set_hs_divider(6); + + let pcr = unsafe { &*crate::peripherals::PCR::PTR }; + unsafe { + pcr.cpu_freq_conf.modify(|_, w| { + w.cpu_hs_div_num() + .bits(((480 / cpu_clock_speed.mhz() / 3) - 1) as u8) + .cpu_hs_120m_force() + .clear_bit() + }); + + pcr.cpu_freq_conf + .modify(|_, w| w.cpu_hs_120m_force().clear_bit()); + + pcr.sysclk_conf.modify(|_, w| { + w.soc_clk_sel().bits(1) // PLL = 1 + }); + ets_update_cpu_frequency(cpu_clock_speed.mhz()); + } +} + +pub(crate) fn esp32c6_rtc_apb_freq_update(apb_freq: ApbClock) { + let lp_aon = unsafe { &*crate::peripherals::LP_AON::ptr() }; + let value = ((apb_freq.hz() >> 12) & u16::MAX as u32) + | (((apb_freq.hz() >> 12) & u16::MAX as u32) << 16); + + lp_aon + .store5 + .modify(|_, w| unsafe { w.lp_aon_store5().bits(value) }); +} + +fn clk_ll_mspi_fast_set_hs_divider(divider: u32) { + // SOC_ROOT_CLK ------> MSPI_FAST_CLK + // HS divider option: 4, 5, 6 (PCR_MSPI_FAST_HS_DIV_NUM=3, 4, 5) + let pcr = unsafe { &*crate::peripherals::PCR::PTR }; + + unsafe { + match divider { + 4 => pcr + .mspi_clk_conf + .modify(|_, w| w.mspi_fast_hs_div_num().bits(3)), + 5 => pcr + .mspi_clk_conf + .modify(|_, w| w.mspi_fast_hs_div_num().bits(4)), + 6 => pcr + .mspi_clk_conf + .modify(|_, w| w.mspi_fast_hs_div_num().bits(5)), + _ => panic!("Unsupported HS MSPI_FAST divider"), + } + } +} + +fn reg_set_bit(reg: u32, bit: u32) { + unsafe { + (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() | bit); + } +} + +fn reg_clr_bit(reg: u32, bit: u32) { + unsafe { + (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !bit); + } +} + +fn reg_write(reg: u32, v: u32) { + unsafe { + (reg as *mut u32).write_volatile(v); + } +} + +fn reg_get_bit(reg: u32, b: u32) -> u32 { + unsafe { (reg as *mut u32).read_volatile() & b } +} + +fn reg_get_field(reg: u32, s: u32, v: u32) -> u32 { + unsafe { ((reg as *mut u32).read_volatile() >> s) & v } +} + +const DR_REG_MODEM_LPCON_BASE: u32 = 0x600AF000; +const MODEM_LPCON_CLK_CONF_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x18; +const MODEM_LPCON_CLK_I2C_MST_EN: u32 = 1 << 2; +const DR_REG_LP_I2C_ANA_MST_BASE: u32 = 0x600B2400; +const LP_I2C_ANA_MST_DATE_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x3fc; +const LP_I2C_ANA_MST_I2C_MAT_CLK_EN: u32 = 1 << 28; +const REGI2C_BIAS: u8 = 0x6a; +const REGI2C_DIG_REG: u8 = 0x6d; +const REGI2C_ULP_CAL: u8 = 0x61; +const REGI2C_SAR_I2C: u8 = 0x69; + +const LP_I2C_ANA_MST_DEVICE_EN_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x14; +const REGI2C_BBPLL_DEVICE_EN: u32 = 1 << 5; +const REGI2C_BIAS_DEVICE_EN: u32 = 1 << 4; +const REGI2C_DIG_REG_DEVICE_EN: u32 = 1 << 8; +const REGI2C_ULP_CAL_DEVICE_EN: u32 = 1 << 6; +const REGI2C_SAR_I2C_DEVICE_EN: u32 = 1 << 7; + +const REGI2C_RTC_SLAVE_ID_V: u8 = 0xFF; +const REGI2C_RTC_SLAVE_ID_S: u8 = 0; +const REGI2C_RTC_ADDR_V: u8 = 0xFF; +const REGI2C_RTC_ADDR_S: u8 = 8; +const REGI2C_RTC_WR_CNTL_V: u8 = 0x1; +const REGI2C_RTC_WR_CNTL_S: u8 = 24; +const REGI2C_RTC_DATA_V: u8 = 0xFF; +const REGI2C_RTC_DATA_S: u8 = 16; + +const LP_I2C_ANA_MST_I2C0_CTRL_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x0; +const LP_I2C_ANA_MST_I2C0_BUSY: u32 = 1 << 25; + +const LP_I2C_ANA_MST_I2C0_DATA_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x8; +const LP_I2C_ANA_MST_I2C0_RDATA_V: u32 = 0x000000FF; +const LP_I2C_ANA_MST_I2C0_RDATA_S: u32 = 0; + +const REGI2C_BBPLL: u8 = 0x66; + +fn regi2c_enable_block(block: u8) { + reg_set_bit(MODEM_LPCON_CLK_CONF_REG, MODEM_LPCON_CLK_I2C_MST_EN); + reg_set_bit(LP_I2C_ANA_MST_DATE_REG, LP_I2C_ANA_MST_I2C_MAT_CLK_EN); + + // Before config I2C register, enable corresponding slave. + match block { + REGI2C_BBPLL => { + reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BBPLL_DEVICE_EN); + } + REGI2C_BIAS => { + reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BIAS_DEVICE_EN); + } + REGI2C_DIG_REG => { + reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_DIG_REG_DEVICE_EN); + } + REGI2C_ULP_CAL => { + reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_ULP_CAL_DEVICE_EN); + } + REGI2C_SAR_I2C => { + reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_SAR_I2C_DEVICE_EN); + } + _ => (), + } +} + +fn regi2c_disable_block(block: u8) { + match block { + REGI2C_BBPLL => { + reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BBPLL_DEVICE_EN); + } + REGI2C_BIAS => { + reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BIAS_DEVICE_EN); + } + REGI2C_DIG_REG => { + reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_DIG_REG_DEVICE_EN); + } + REGI2C_ULP_CAL => { + reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_ULP_CAL_DEVICE_EN); + } + REGI2C_SAR_I2C => { + reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_SAR_I2C_DEVICE_EN); + } + _ => (), + } +} + +pub(crate) fn regi2c_write(block: u8, _host_id: u8, reg_add: u8, data: u8) { + regi2c_enable_block(block); + + let temp: u32 = ((block as u32 & REGI2C_RTC_SLAVE_ID_V as u32) << REGI2C_RTC_SLAVE_ID_S as u32) + | ((reg_add as u32 & REGI2C_RTC_ADDR_V as u32) << REGI2C_RTC_ADDR_S as u32) + | ((0x1 & REGI2C_RTC_WR_CNTL_V as u32) << REGI2C_RTC_WR_CNTL_S as u32) // 0: READ I2C register; 1: Write I2C register; + | (((data as u32) & REGI2C_RTC_DATA_V as u32) << REGI2C_RTC_DATA_S as u32); + reg_write(LP_I2C_ANA_MST_I2C0_CTRL_REG, temp); + while reg_get_bit(LP_I2C_ANA_MST_I2C0_CTRL_REG, LP_I2C_ANA_MST_I2C0_BUSY) != 0 {} + + regi2c_disable_block(block); +} + +pub(crate) fn regi2c_write_mask(block: u8, _host_id: u8, reg_add: u8, msb: u8, lsb: u8, data: u8) { + assert!(msb - lsb < 8); + regi2c_enable_block(block); + + // Read the i2c bus register + let mut temp: u32 = ((block as u32 & REGI2C_RTC_SLAVE_ID_V as u32) + << REGI2C_RTC_SLAVE_ID_S as u32) + | (reg_add as u32 & REGI2C_RTC_ADDR_V as u32) << REGI2C_RTC_ADDR_S as u32; + reg_write(LP_I2C_ANA_MST_I2C0_CTRL_REG, temp); + while reg_get_bit(LP_I2C_ANA_MST_I2C0_CTRL_REG, LP_I2C_ANA_MST_I2C0_BUSY) != 0 {} + temp = reg_get_field( + LP_I2C_ANA_MST_I2C0_DATA_REG, + LP_I2C_ANA_MST_I2C0_RDATA_S, + LP_I2C_ANA_MST_I2C0_RDATA_V, + ); + // Write the i2c bus register + temp &= (!(0xFFFFFFFF << lsb)) | (0xFFFFFFFF << (msb + 1)); + temp = + ((data as u32 & (!(0xFFFFFFFF << (msb as u32 - lsb as u32 + 1)))) << (lsb as u32)) | temp; + temp = ((block as u32 & REGI2C_RTC_SLAVE_ID_V as u32) << REGI2C_RTC_SLAVE_ID_S as u32) + | ((reg_add as u32 & REGI2C_RTC_ADDR_V as u32) << REGI2C_RTC_ADDR_S as u32) + | ((0x1 & REGI2C_RTC_WR_CNTL_V as u32) << REGI2C_RTC_WR_CNTL_S as u32) + | ((temp & REGI2C_RTC_DATA_V as u32) << REGI2C_RTC_DATA_S as u32); + reg_write(LP_I2C_ANA_MST_I2C0_CTRL_REG, temp); + while reg_get_bit(LP_I2C_ANA_MST_I2C0_CTRL_REG, LP_I2C_ANA_MST_I2C0_BUSY) != 0 {} + + regi2c_disable_block(block); +} diff --git a/esp-hal-common/src/clock/mod.rs b/esp-hal-common/src/clock/mod.rs index fa8318e9ac5..2503dedcc3e 100644 --- a/esp-hal-common/src/clock/mod.rs +++ b/esp-hal-common/src/clock/mod.rs @@ -9,9 +9,10 @@ use crate::{ #[cfg_attr(esp32, path = "clocks_ll/esp32.rs")] #[cfg_attr(esp32c2, path = "clocks_ll/esp32c2.rs")] #[cfg_attr(esp32c3, path = "clocks_ll/esp32c3.rs")] +#[cfg_attr(esp32c6, path = "clocks_ll/esp32c6.rs")] #[cfg_attr(esp32s2, path = "clocks_ll/esp32s2.rs")] #[cfg_attr(esp32s3, path = "clocks_ll/esp32s3.rs")] -mod clocks_ll; +pub(crate) mod clocks_ll; pub trait Clock { fn frequency(&self) -> HertzU32; @@ -33,7 +34,7 @@ pub enum CpuClock { Clock120MHz, #[cfg(not(esp32c2))] Clock160MHz, - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] Clock240MHz, } @@ -46,7 +47,7 @@ impl Clock for CpuClock { CpuClock::Clock120MHz => HertzU32::MHz(120), #[cfg(not(esp32c2))] CpuClock::Clock160MHz => HertzU32::MHz(160), - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] CpuClock::Clock240MHz => HertzU32::MHz(240), } } @@ -83,7 +84,7 @@ impl Clock for XtalClock { #[allow(unused)] #[derive(Debug, Clone, Copy)] pub(crate) enum PllClock { - #[cfg(not(esp32c2))] + #[cfg(not(any(esp32c2, esp32c6)))] Pll320MHz, Pll480MHz, } @@ -120,6 +121,8 @@ pub struct Clocks<'d> { pub pwm_clock: HertzU32, #[cfg(esp32s3)] pub crypto_pwm_clock: HertzU32, + #[cfg(esp32c6)] + pub crypto_clock: HertzU32, // TODO chip specific additional ones as needed } @@ -143,6 +146,8 @@ impl<'d> Clocks<'d> { pwm_clock: raw_clocks.pwm_clock, #[cfg(esp32s3)] crypto_pwm_clock: raw_clocks.crypto_pwm_clock, + #[cfg(esp32c6)] + crypto_clock: raw_clocks.crypto_clock, } } } @@ -157,6 +162,8 @@ pub struct RawClocks { pub pwm_clock: HertzU32, #[cfg(esp32s3)] pub crypto_pwm_clock: HertzU32, + #[cfg(esp32c6)] + pub crypto_clock: HertzU32, // TODO chip specific additional ones as needed } @@ -368,6 +375,60 @@ impl<'d> ClockControl<'d> { } } +#[cfg(esp32c6)] +impl<'d> ClockControl<'d> { + /// Use what is considered the default settings after boot. + #[allow(unused)] + pub fn boot_defaults( + clock_control: impl Peripheral

+ 'd, + ) -> ClockControl<'d> { + ClockControl { + _private: clock_control.into_ref(), + desired_rates: RawClocks { + cpu_clock: HertzU32::MHz(80), + apb_clock: HertzU32::MHz(80), + xtal_clock: HertzU32::MHz(40), + i2c_clock: HertzU32::MHz(40), + crypto_clock: HertzU32::MHz(160), + }, + } + } + + /// Configure the CPU clock speed. + #[allow(unused)] + pub fn configure( + clock_control: impl Peripheral

+ 'd, + cpu_clock_speed: CpuClock, + ) -> ClockControl<'d> { + let apb_freq; + let xtal_freq = XtalClock::RtcXtalFreq40M; + let pll_freq = PllClock::Pll480MHz; + + if cpu_clock_speed.mhz() <= xtal_freq.mhz() { + apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); + clocks_ll::esp32c6_rtc_update_to_xtal(xtal_freq, 1); + clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq); + } else { + apb_freq = ApbClock::ApbFreq80MHz; + clocks_ll::esp32c6_rtc_bbpll_enable(); + clocks_ll::esp32c6_rtc_bbpll_configure(xtal_freq, pll_freq); + clocks_ll::esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed); + clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq); + } + + ClockControl { + _private: clock_control.into_ref(), + desired_rates: RawClocks { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: apb_freq.frequency(), + xtal_clock: xtal_freq.frequency(), + i2c_clock: HertzU32::MHz(40), + crypto_clock: HertzU32::MHz(160), + }, + } + } +} + #[cfg(esp32s2)] impl<'d> ClockControl<'d> { /// Use what is considered the default settings after boot. diff --git a/esp-hal-common/src/dma/gdma.rs b/esp-hal-common/src/dma/gdma.rs index 1eda25228fe..82dfc285773 100644 --- a/esp-hal-common/src/dma/gdma.rs +++ b/esp-hal-common/src/dma/gdma.rs @@ -36,7 +36,7 @@ macro_rules! impl_channel { fn clear_out_interrupts() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] dma.[].write(|w| { w.out_eof() .set_bit() @@ -52,6 +52,22 @@ macro_rules! impl_channel { .set_bit() }); + #[cfg(esp32c6)] + dma.[].write(|w| { + w.out_eof() + .set_bit() + .out_dscr_err() + .set_bit() + .out_done() + .set_bit() + .out_total_eof() + .set_bit() + .outfifo_ovf() + .set_bit() + .outfifo_udf() + .set_bit() + }); + #[cfg(esp32s3)] dma.[].write(|w| { w.out_eof() @@ -89,9 +105,9 @@ macro_rules! impl_channel { fn has_out_descriptor_error() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] let ret = dma.[].read().out_dscr_err().bit(); - #[cfg(esp32s3)] + #[cfg(any(esp32c6, esp32s3))] let ret = dma.[].read().out_dscr_err().bit(); ret @@ -112,9 +128,9 @@ macro_rules! impl_channel { fn is_out_done() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] let ret = dma.[].read().out_total_eof().bit(); - #[cfg(esp32s3)] + #[cfg(any(esp32c6, esp32s3))] let ret = dma.[].read().out_total_eof().bit(); ret @@ -128,9 +144,9 @@ macro_rules! impl_channel { fn is_out_eof_interrupt_set() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] let ret = dma.[].read().out_eof().bit(); - #[cfg(esp32s3)] + #[cfg(any(esp32c6, esp32s3))] let ret = dma.[].read().out_eof().bit(); ret @@ -139,13 +155,13 @@ macro_rules! impl_channel { fn reset_out_eof_interrupt() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] dma.[].write(|w| { w.out_eof() .set_bit() }); - #[cfg(esp32s3)] + #[cfg(any(esp32c6, esp32s3))] dma.[].write(|w| { w.out_eof() .set_bit() @@ -171,7 +187,7 @@ macro_rules! impl_channel { fn clear_in_interrupts() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] dma.[].write(|w| { w.in_suc_eof() .set_bit() @@ -189,6 +205,24 @@ macro_rules! impl_channel { .set_bit() }); + #[cfg(esp32c6)] + dma.[].write(|w| { + w.in_suc_eof() + .set_bit() + .in_err_eof() + .set_bit() + .in_dscr_err() + .set_bit() + .in_dscr_empty() + .set_bit() + .in_done() + .set_bit() + .infifo_ovf() + .set_bit() + .infifo_udf() + .set_bit() + }); + #[cfg(esp32s3)] dma.[].write(|w| { w.in_suc_eof() @@ -228,9 +262,9 @@ macro_rules! impl_channel { fn has_in_descriptor_error() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] let ret = dma.[].read().in_dscr_err().bit(); - #[cfg(esp32s3)] + #[cfg(any(esp32c6, esp32s3))] let ret = dma.[].read().in_dscr_err().bit(); ret @@ -251,9 +285,9 @@ macro_rules! impl_channel { fn is_in_done() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(esp32s3))] + #[cfg(not(any(esp32c6, esp32s3)))] let ret = dma.[].read().in_suc_eof().bit(); - #[cfg(esp32s3)] + #[cfg(any(esp32c6, esp32s3))] let ret = dma.[].read().in_suc_eof().bit(); ret @@ -267,17 +301,18 @@ macro_rules! impl_channel { fn is_listening_in_eof() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(esp32s3)] { + if #[cfg(any(esp32c6, esp32s3))] { dma.[].read().in_suc_eof().bit_is_set() } else { dma.[].read().in_suc_eof().bit_is_set() } } } + fn is_listening_out_eof() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(esp32s3)] { + if #[cfg(any(esp32c6, esp32s3))] { dma.[].read().out_total_eof().bit_is_set() } else { dma.[].read().out_total_eof().bit_is_set() @@ -288,37 +323,40 @@ macro_rules! impl_channel { fn listen_in_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(esp32s3)] { + if #[cfg(any(esp32c6, esp32s3))] { dma.[].modify(|_, w| w.in_suc_eof().set_bit()) } else { dma.[].modify(|_, w| w.in_suc_eof().set_bit()) } } } + fn listen_out_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(esp32s3)] { + if #[cfg(any(esp32c6, esp32s3))] { dma.[].modify(|_, w| w.out_total_eof().set_bit()) } else { dma.[].modify(|_, w| w.out_total_eof().set_bit()) } } } + fn unlisten_in_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(esp32s3)] { + if #[cfg(any(esp32c6, esp32s3))] { dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) } else { dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) } } } + fn unlisten_out_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(esp32s3)] { + if #[cfg(any(esp32c6, esp32s3))] { dma.[].modify(|_, w| w.out_total_eof().clear_bit()) } else { dma.[].modify(|_, w| w.out_total_eof().clear_bit()) diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index b723b9be2f3..6574c2ae035 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -54,19 +54,19 @@ pub enum DmaPeripheral { Spi2 = 0, #[cfg(any(pdma, esp32s3))] Spi3 = 1, - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] Uhci0 = 2, - #[cfg(any(esp32, esp32s2, esp32c3, esp32s3))] + #[cfg(any(esp32, esp32s2, esp32c3, esp32c6, esp32s3))] I2s0 = 3, #[cfg(any(esp32, esp32s3))] I2s1 = 4, #[cfg(esp32s3)] LcdCam = 5, - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] Aes = 6, #[cfg(gdma)] Sha = 7, - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] Adc = 8, #[cfg(esp32s3)] Rmt = 9, @@ -1024,6 +1024,77 @@ pub(crate) mod asynch { } } + #[cfg(esp32c6)] + mod interrupt { + use super::*; + + #[interrupt] + fn DMA_IN_CH0() { + use crate::dma::gdma::{Channel0 as Channel, Channel0RxImpl as ChannelRxImpl}; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_OUT_CH0() { + use crate::dma::gdma::{Channel0 as Channel, Channel0TxImpl as ChannelTxImpl}; + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_IN_CH1() { + use crate::dma::gdma::{Channel1 as Channel, Channel1RxImpl as ChannelRxImpl}; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_OUT_CH1() { + use crate::dma::gdma::{Channel1 as Channel, Channel1TxImpl as ChannelTxImpl}; + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_IN_CH2() { + use crate::dma::gdma::{Channel2 as Channel, Channel2RxImpl as ChannelRxImpl}; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_OUT_CH2() { + use crate::dma::gdma::{Channel2 as Channel, Channel2TxImpl as ChannelTxImpl}; + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + } + #[cfg(esp32s3)] mod interrupt { use super::*; @@ -1117,7 +1188,7 @@ pub(crate) mod asynch { } } - #[cfg(any(esp32s2, esp32))] + #[cfg(any(esp32, esp32s2))] mod interrupt { use super::*; diff --git a/esp-hal-common/src/efuse/esp32c6.rs b/esp-hal-common/src/efuse/esp32c6.rs new file mode 100644 index 00000000000..81ae4ce5f84 --- /dev/null +++ b/esp-hal-common/src/efuse/esp32c6.rs @@ -0,0 +1,62 @@ +//! Reading of eFuses + +use crate::peripherals::EFUSE; + +pub struct Efuse; + +impl Efuse { + /// Reads chip's MAC address from the eFuse storage. + /// + /// # Example + /// + /// ``` + /// let mac_address = Efuse::get_mac_address(); + /// writeln!( + /// serial_tx, + /// "MAC: {:#X}:{:#X}:{:#X}:{:#X}:{:#X}:{:#X}", + /// mac_address[0], + /// mac_address[1], + /// mac_address[2], + /// mac_address[3], + /// mac_address[4], + /// mac_address[5] + /// ); + /// ``` + pub fn get_mac_address() -> [u8; 6] { + let efuse = unsafe { &*EFUSE::ptr() }; + + let mac_low: u32 = efuse.rd_mac_spi_sys_0.read().mac_0().bits(); + let mac_high: u32 = efuse.rd_mac_spi_sys_1.read().mac_1().bits() as u32; + + let mac_low_bytes = mac_low.to_be_bytes(); + let mac_high_bytes = mac_high.to_be_bytes(); + + [ + mac_high_bytes[2], + mac_high_bytes[3], + mac_low_bytes[0], + mac_low_bytes[1], + mac_low_bytes[2], + mac_low_bytes[3], + ] + } + + /// Get status of SPI boot encryption. + pub fn get_flash_encryption() -> bool { + let efuse = unsafe { &*EFUSE::ptr() }; + (efuse + .rd_repeat_data1 + .read() + .spi_boot_crypt_cnt() + .bits() + .count_ones() + % 2) + != 0 + } + + /// Get the multiplier for the timeout value of the RWDT STAGE 0 register. + pub fn get_rwdt_multiplier() -> u8 { + let efuse = unsafe { &*EFUSE::ptr() }; + efuse.rd_repeat_data1.read().wdt_delay_sel().bits() + } +} diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index 299fc0185f2..bf52bac93df 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -14,6 +14,7 @@ use core::marker::PhantomData; #[cfg_attr(esp32, path = "gpio/esp32.rs")] #[cfg_attr(esp32c2, path = "gpio/esp32c2.rs")] #[cfg_attr(esp32c3, path = "gpio/esp32c3.rs")] +#[cfg_attr(esp32c6, path = "gpio/esp32c6.rs")] #[cfg_attr(esp32s2, path = "gpio/esp32s2.rs")] #[cfg_attr(esp32s3, path = "gpio/esp32s3.rs")] pub mod types; @@ -275,7 +276,7 @@ impl InteruptStatusRegisterAccess for SingleCoreInteruptStatusRegisterAccessBank // interrupt enable bit see // https://github.com/espressif/esp-idf/blob/c04803e88b871a4044da152dfb3699cf47354d18/components/hal/esp32s3/include/hal/gpio_ll.h#L32 // Treating it as SingleCore in the gpio macro makes this work. -#[cfg(not(any(esp32c2, esp32c3, esp32s2, esp32s3)))] +#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s2, esp32s3)))] impl InteruptStatusRegisterAccess for DualCoreInteruptStatusRegisterAccessBank0 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int.read().bits() @@ -298,7 +299,7 @@ impl InteruptStatusRegisterAccess for DualCoreInteruptStatusRegisterAccessBank0 // interrupt enable bit see // https://github.com/espressif/esp-idf/blob/c04803e88b871a4044da152dfb3699cf47354d18/components/hal/esp32s3/include/hal/gpio_ll.h#L32 // Treating it as SingleCore in the gpio macro makes this work. -#[cfg(not(any(esp32c2, esp32c3, esp32s2, esp32s3)))] +#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s2, esp32s3)))] impl InteruptStatusRegisterAccess for DualCoreInteruptStatusRegisterAccessBank1 { fn pro_cpu_interrupt_status_read() -> u32 { unsafe { &*GPIO::PTR }.pcpu_int1.read().bits() @@ -462,7 +463,7 @@ impl BankGpioRegisterAccess for Bank0GpioRegisterAccess { } } -#[cfg(not(any(esp32c2, esp32c3)))] +#[cfg(not(any(esp32c2, esp32c3, esp32c6)))] impl BankGpioRegisterAccess for Bank1GpioRegisterAccess { fn write_out_en_clear(word: u32) { unsafe { &*GPIO::PTR } @@ -1692,7 +1693,7 @@ pub fn enable_iomux_clk_gate() { } } -#[cfg(not(any(esp32c2, esp32c3, esp32s2)))] +#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s2)))] #[doc(hidden)] #[macro_export] macro_rules! analog { @@ -1810,7 +1811,7 @@ macro_rules! analog { } } -#[cfg(any(esp32c2, esp32c3))] +#[cfg(any(esp32c2, esp32c3, esp32c6))] #[doc(hidden)] #[macro_export] macro_rules! analog { diff --git a/esp-hal-common/src/gpio/esp32c3.rs b/esp-hal-common/src/gpio/esp32c3.rs index 12b67709b1d..4a7a3490f02 100644 --- a/esp-hal-common/src/gpio/esp32c3.rs +++ b/esp-hal-common/src/gpio/esp32c3.rs @@ -97,7 +97,7 @@ pub enum OutputSignal { I2S_MCLK = 12, I2SO_BCK = 13, I2SO_WS = 14, - I2SI_SD = 15, + I2SO_SD = 15, I2SI_BCK = 16, I2SI_WS = 17, GPIO_WLAN_PRIO = 18, diff --git a/esp-hal-common/src/gpio/esp32c6.rs b/esp-hal-common/src/gpio/esp32c6.rs new file mode 100644 index 00000000000..ca8bb15c791 --- /dev/null +++ b/esp-hal-common/src/gpio/esp32c6.rs @@ -0,0 +1,287 @@ +use paste::paste; + +use crate::{ + gpio::PhantomData, + peripherals::GPIO, + AlternateFunction, + Bank0GpioRegisterAccess, + GpioPin, + InputOutputAnalogPinType, + InputOutputPinType, + Unknown, +}; + +pub const NUM_PINS: usize = 30; + +pub type OutputSignalType = u8; +pub const OUTPUT_SIGNAL_MAX: u8 = 128; +pub const INPUT_SIGNAL_MAX: u8 = 124; + +pub const ONE_INPUT: u8 = 0x1e; +pub const ZERO_INPUT: u8 = 0x1f; + +pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; + +pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { + unsafe { &(&*crate::peripherals::IO_MUX::PTR).gpio[gpio_num as usize] } +} + +pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { + int_enable as u8 | ((nmi_enable as u8) << 1) +} + +/// Peripheral input signals for the GPIO mux +#[allow(non_camel_case_types)] +#[derive(PartialEq, Copy, Clone)] +pub enum InputSignal { + EXT_ADC_START = 0, + U0RXD = 6, + U0CTS = 7, + U0DSR = 8, + U1RXD = 9, + U1CTS = 10, + U1DSR = 11, + I2S_MCLK = 12, + I2SO_BCK = 13, + I2SO_WS = 14, + I2SI_SD = 15, + I2SI_BCK = 16, + I2SI_WS = 17, + USB_JTAG_TDO_BRIDGE = 19, + CPU_TESTBUS0 = 20, // TODO: verify + CPU_GPIO_IN0 = 28, + CPU_GPIO_IN1 = 29, + CPU_GPIO_IN2 = 30, + CPU_GPIO_IN3 = 31, + CPU_GPIO_IN4 = 32, + CPU_GPIO_IN5 = 33, + CPU_GPIO_IN6 = 34, + CPU_GPIO_IN7 = 35, + USB_JTAG_TMS = 37, + USB_EXTPHY_OEN = 40, + USB_EXTPHY_VM = 41, + USB_EXTPHY_VPO = 42, + I2CEXT0_SCL = 45, + I2CEXT0_SDA = 46, + PARL_RX_DATA0 = 47, + PARL_RX_DATA1 = 48, + PARL_RX_DATA2 = 49, + PARL_RX_DATA3 = 50, + PARL_RX_DATA4 = 51, + PARL_RX_DATA5 = 52, + PARL_RX_DATA6 = 53, + PARL_RX_DATA7 = 54, + PARL_RX_DATA8 = 55, + PARL_RX_DATA9 = 56, + PARL_RX_DATA10 = 57, + PARL_RX_DATA11 = 58, + PARL_RX_DATA12 = 59, + PARL_RX_DATA13 = 60, + PARL_RX_DATA14 = 61, + PARL_RX_DATA15 = 62, + FSPICLK = 63, + FSPIQ = 64, + FSPID = 65, + FSPIHD = 66, + FSPIWP = 67, + FSPICS0 = 68, + PARL_RX_CLK = 69, + PARL_TX_CLK = 70, + RMT_SIG_0 = 71, + RMT_SIG_1 = 72, + TWAI0_RX = 73, + TWAI1_RX = 77, + PWM0_SYNC0 = 87, + PWM0_SYNC1 = 88, + PWM0_SYNC2 = 89, + PWM0_F0 = 90, + PWM0_F1 = 91, + PWM0_F2 = 92, + PWM0_CAP0 = 93, + PWM0_CAP1 = 94, + PWM0_CAP2 = 95, + SIG_IN_FUNC97 = 97, + SIG_IN_FUNC98 = 98, + SIG_IN_FUNC99 = 99, + SIG_IN_FUNC100 = 100, + PCNT0_SIG_CH0 = 101, + PCNT0_SIG_CH1 = 102, + PCNT0_CTRL_CH0 = 103, + PCNT0_CTRL_CH1 = 104, + PCNT1_SIG_CH0 = 105, + PCNT1_SIG_CH1 = 106, + PCNT1_CTRL_CH0 = 107, + PCNT1_CTRL_CH1 = 108, + PCNT2_SIG_CH0 = 109, + PCNT2_SIG_CH1 = 110, + PCNT2_CTRL_CH0 = 111, + PCNT2_CTRL_CH1 = 112, + PCNT3_SIG_CH0 = 113, + PCNT3_SIG_CH1 = 114, + PCNT3_CTRL_CH0 = 115, + PCNT3_CTRL_CH1 = 116, + SPIQ = 121, + SPID = 122, + SPIHD = 123, + SPIWP = 124, +} + +/// Peripheral input signals for the GPIO mux +#[allow(non_camel_case_types)] +#[derive(PartialEq, Copy, Clone)] +pub enum OutputSignal { + LEDC_LS_SIG0 = 0, + LEDC_LS_SIG1 = 1, + LEDC_LS_SIG2 = 2, + LEDC_LS_SIG3 = 3, + LEDC_LS_SIG4 = 4, + LEDC_LS_SIG5 = 5, + U0TXD = 6, + U0RTS = 7, + U0DTR = 8, + U1TXD = 9, + U1RTS = 10, + U1DTR = 11, + I2S_MCLK = 12, + I2SO_BCK = 13, + I2SO_WS = 14, + I2SO_SD = 15, + I2SI_BCK = 16, + I2SI_WS = 17, + I2SO_SD1 = 18, + USB_JTAG_TRST = 19, // TODO: Verify + CPU_GPIO_OUT0 = 28, + CPU_GPIO_OUT1 = 29, + CPU_GPIO_OUT2 = 30, + CPU_GPIO_OUT3 = 31, + CPU_GPIO_OUT4 = 32, + CPU_GPIO_OUT5 = 33, + CPU_GPIO_OUT6 = 34, + CPU_GPIO_OUT7 = 35, + USB_JTAG_TCK = 36, + USB_JTAG_TMS = 37, + USB_JTAG_TDI = 38, + USB_JTAG_TDO = 39, + I2CEXT0_SCL = 45, + I2CEXT0_SDA = 46, + PARL_TX_DATA0 = 47, + PARL_TX_DATA1 = 48, + PARL_TX_DATA2 = 49, + PARL_TX_DATA3 = 50, + PARL_TX_DATA4 = 51, + PARL_TX_DATA5 = 52, + PARL_TX_DATA6 = 53, + PARL_TX_DATA7 = 54, + PARL_TX_DATA8 = 55, + PARL_TX_DATA9 = 56, + PARL_TX_DATA10 = 57, + PARL_TX_DATA11 = 58, + PARL_TX_DATA12 = 59, + PARL_TX_DATA13 = 60, + PARL_TX_DATA14 = 61, + PARL_TX_DATA15 = 62, + FSPICLK_MUX = 63, + FSPIQ = 64, + FSPID = 65, + FSPIHD = 66, + FSPIWP = 67, + FSPICS0 = 68, + SDIO_TOHOST_INT = 69, + PARL_TX_CLK = 70, + RMT_SIG_0 = 71, + RMT_SIG_1 = 72, + TWAI0_TX = 73, + TWAI0_BUS_OFF_ON = 74, + TWAI0_CLKOUT = 75, + TWAI0_STANDBY = 76, + TWAI1_TX = 77, + TWAI1_BUS_OFF_ON = 78, + TWAI1_CLKOUT = 79, + TWAI1_STANDBY = 80, + GPIO_SD0 = 83, + GPIO_SD1 = 84, + GPIO_SD2 = 85, + GPIO_SD3 = 86, + PWM0_0A = 87, + PWM0_0B = 88, + PWM0_1A = 89, + PWM0_1B = 90, + PWM0_2A = 91, + PWM0_2B = 92, + SIG_IN_FUNC97 = 97, + SIG_IN_FUNC98 = 98, + SIG_IN_FUNC99 = 99, + SIG_IN_FUNC100 = 100, + FSPICS1 = 101, + FSPICS2 = 102, + FSPICS3 = 103, + FSPICS4 = 104, + FSPICS5 = 105, + SPICLK_MUX = 114, + SPICS0 = 115, + SPICS1 = 116, + GPIO_TASK_MATRIX_OUT0 = 117, // TODO: verify rhis group - not in TRM but in ESP_IDF + GPIO_TASK_MATRIX_OUT1 = 118, + GPIO_TASK_MATRIX_OUT2 = 119, + GPIO_TASK_MATRIX_OUT3 = 120, + SPIQ = 121, + SPID = 122, + SPIHD = 123, + SPIWP = 124, + CLK_OUT_OUT1 = 125, + CLK_OUT_OUT2 = 126, + CLK_OUT_OUT3 = 127, + GPIO = 128, +} + +crate::gpio::gpio! { + Single, + (0, 0, InputOutputAnalog) + (1, 0, InputOutputAnalog) + (2, 0, InputOutputAnalog (2 => FSPIQ) (2 => FSPIQ)) + (3, 0, InputOutputAnalog) + (4, 0, InputOutputAnalog (2 => FSPIHD) (0 => USB_JTAG_TMS 2 => FSPIHD)) + (5, 0, InputOutputAnalog (2 => FSPIWP) (0 => USB_JTAG_TDI 2 => FSPIWP)) + (6, 0, InputOutputAnalog (2 => FSPICLK) (0 => USB_JTAG_TCK 2 => FSPICLK_MUX)) + (7, 0, InputOutputAnalog (2 => FSPID) (0 => USB_JTAG_TDO 2 => FSPID)) + (8, 0, InputOutput) + (9, 0, InputOutput) + (10, 0, InputOutput) + (11, 0, InputOutput) + (12, 0, InputOutput) + (13, 0, InputOutput) + (14, 0, InputOutput) + (15, 0, InputOutput) + (16, 0, InputOutput (0 => U0RXD) (2 => FSPICS0)) + (17, 0, InputOutput () (0 => U0TXD 2 => FSPICS1)) + (18, 0, InputOutput () (2 => FSPICS2)) // TODO 0 => SDIO_CMD + (19, 0, InputOutput () (2 => FSPICS3)) // TODO 0 => SDIO_CLK + (20, 0, InputOutput () (2 => FSPICS4)) //TODO 0 => SDIO_DATA0 + (21, 0, InputOutput () (2 => FSPICS5)) // TODO 0 => SDIO_DATA1 + (22, 0, InputOutput () ()) // TODO 0 => SDIO_DATA2 + (23, 0, InputOutput () ()) // TODO 0 => SDIO_DATA3 + (24, 0, InputOutput () (0 => SPICS0)) + (25, 0, InputOutput (0 => SPIQ) (0 => SPIQ)) + (26, 0, InputOutput (0 => SPIWP) (0 => SPIWP)) + (27, 0, InputOutput) + (28, 0, InputOutput (0 => SPIHD) (0 => SPIHD)) + (29, 0, InputOutput () (0 => SPICLK_MUX)) + (30, 0, InputOutput (0 => SPID) (0 => SPID)) +} + +crate::gpio::analog! { + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 +} + +// TODO USB pins +// implement marker traits on USB pins +// impl crate::otg_fs::UsbSel for Gpio?? {} +// impl crate::otg_fs::UsbDp for Gpio12 {} +// impl crate::otg_fs::UsbDm for Gpio13 {} diff --git a/esp-hal-common/src/i2c.rs b/esp-hal-common/src/i2c.rs index e6cde248b18..4d18d301d0f 100644 --- a/esp-hal-common/src/i2c.rs +++ b/esp-hal-common/src/i2c.rs @@ -136,7 +136,7 @@ enum Ack { Nack, } -#[cfg(any(esp32c2, esp32c3, esp32s3))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] enum Opcode { RStart = 6, Write = 1, @@ -352,7 +352,7 @@ pub trait Instance { self.set_frequency(clocks.i2c_clock.convert(), frequency); // Propagate configuration changes (only necessary with C2, C3, and S3) - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] self.register_block() .ctr .modify(|_, w| w.conf_upgate().set_bit()); @@ -553,7 +553,7 @@ pub trait Instance { ); } - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] /// Sets the frequency of the I2C interface by calculating and applying the /// associated timings - corresponds to i2c_ll_cal_bus_clk and /// i2c_ll_set_bus_timing in ESP-IDF @@ -641,7 +641,7 @@ pub trait Instance { ) { unsafe { // divider - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] self.register_block().clk_conf.modify(|_, w| { w.sclk_sel() .clear_bit() @@ -962,7 +962,7 @@ pub trait Instance { fn update_config(&self) { // Ensure that the configuration of the peripheral is correctly propagated // (only necessary for C3 and S3 variant) - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] self.register_block() .ctr .modify(|_, w| w.conf_upgate().set_bit()); diff --git a/esp-hal-common/src/i2s.rs b/esp-hal-common/src/i2s.rs index 90b5670ccf2..075f9cde623 100644 --- a/esp-hal-common/src/i2s.rs +++ b/esp-hal-common/src/i2s.rs @@ -17,7 +17,7 @@ use crate::{ #[cfg(any(esp32, esp32s2, esp32s3))] const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 6; -#[cfg(any(esp32c3))] +#[cfg(any(esp32c3, esp32c6))] const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 9; const I2S_LL_MCLK_DIVIDER_MAX: usize = (1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1; @@ -990,13 +990,15 @@ mod private { use super::{DataFormat, I2sRx, I2sTx, RegisterAccess, Standard, I2S_LL_MCLK_DIVIDER_MAX}; #[cfg(any(esp32c3, esp32s2))] use crate::peripherals::i2s::RegisterBlock; + #[cfg(esp32c6)] + use crate::peripherals::i2s0::RegisterBlock; // on ESP32-S3 I2S1 doesn't support all features - use that to avoid using those features // by accident #[cfg(any(esp32s3, esp32))] use crate::peripherals::i2s1::RegisterBlock; #[cfg(any(esp32c3, esp32s2))] use crate::peripherals::I2S; - #[cfg(any(esp32s3, esp32))] + #[cfg(any(esp32s3, esp32, esp32c6))] use crate::peripherals::I2S0 as I2S; use crate::{ clock::Clocks, @@ -1289,8 +1291,9 @@ mod private { } } - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] pub trait RegisterAccessPrivate: Signals + RegBlock { + #[cfg(any(esp32c3, esp32s3))] fn set_clock(&self, clock_settings: I2sClockDividers) { let i2s = self.register_block(); @@ -1390,6 +1393,105 @@ mod private { }); } + #[cfg(any(esp32c6))] + fn set_clock(&self, clock_settings: I2sClockDividers) { + let i2s = self.register_block(); + let pcr = unsafe { &*esp32c6::PCR::PTR }; // I2S clocks are configured via PCR + + let clkm_div_x: u32; + let clkm_div_y: u32; + let clkm_div_z: u32; + let clkm_div_yn1: u32; + + if clock_settings.denominator == 0 || clock_settings.numerator == 0 { + clkm_div_x = 0; + clkm_div_y = 0; + clkm_div_z = 0; + clkm_div_yn1 = 1; + } else { + if clock_settings.numerator > clock_settings.denominator / 2 { + clkm_div_x = clock_settings + .denominator + .overflowing_div( + clock_settings + .denominator + .overflowing_sub(clock_settings.numerator) + .0, + ) + .0 + .overflowing_sub(1) + .0; + clkm_div_y = clock_settings.denominator + % (clock_settings + .denominator + .overflowing_sub(clock_settings.numerator) + .0); + clkm_div_z = clock_settings + .denominator + .overflowing_sub(clock_settings.numerator) + .0; + clkm_div_yn1 = 1; + } else { + clkm_div_x = clock_settings.denominator / clock_settings.numerator - 1; + clkm_div_y = clock_settings.denominator % clock_settings.numerator; + clkm_div_z = clock_settings.numerator; + clkm_div_yn1 = 0; + } + } + + pcr.i2s_tx_clkm_div_conf.modify(|_, w| { + w.i2s_tx_clkm_div_x() + .variant(clkm_div_x as u16) + .i2s_tx_clkm_div_y() + .variant(clkm_div_y as u16) + .i2s_tx_clkm_div_yn1() + .variant(if clkm_div_yn1 != 0 { true } else { false }) + .i2s_tx_clkm_div_z() + .variant(clkm_div_z as u16) + }); + + pcr.i2s_tx_clkm_conf.modify(|_, w| { + w.i2s_tx_clkm_en() + .set_bit() + .i2s_tx_clkm_sel() + .variant(2) // for now fixed at 160MHz + .i2s_tx_clkm_div_num() + .variant(clock_settings.mclk_divider as u8) + }); + + i2s.tx_conf1.modify(|_, w| { + w.tx_bck_div_num() + .variant((clock_settings.bclk_divider - 1) as u8) + }); + + pcr.i2s_rx_clkm_div_conf.modify(|_, w| { + w.i2s_rx_clkm_div_x() + .variant(clkm_div_x as u16) + .i2s_rx_clkm_div_y() + .variant(clkm_div_y as u16) + .i2s_rx_clkm_div_yn1() + .variant(if clkm_div_yn1 != 0 { true } else { false }) + .i2s_rx_clkm_div_z() + .variant(clkm_div_z as u16) + }); + + pcr.i2s_rx_clkm_conf.modify(|_, w| { + w.i2s_rx_clkm_en() + .set_bit() + .i2s_rx_clkm_sel() + .variant(2) // for now fixed at 160MHz + .i2s_rx_clkm_div_num() + .variant(clock_settings.mclk_divider as u8) + .i2s_mclk_sel() + .variant(true) + }); + + i2s.rx_conf1.modify(|_, w| { + w.rx_bck_div_num() + .variant((clock_settings.bclk_divider - 1) as u8) + }); + } + fn configure(&self, _standard: &Standard, data_format: &DataFormat) { let i2s = self.register_block(); i2s.tx_conf1.modify(|_, w| { @@ -1609,7 +1711,7 @@ mod private { #[derive(Clone)] pub struct I2sPeripheral1 {} - #[cfg(esp32c3)] + #[cfg(any(esp32c3, esp32c6))] impl Signals for I2sPeripheral0 { fn get_peripheral(&self) -> Peripheral { Peripheral::I2s0 @@ -1632,7 +1734,7 @@ mod private { } fn dout_signal(&self) -> OutputSignal { - OutputSignal::I2SI_SD + OutputSignal::I2SO_SD } fn bclk_rx_signal(&self) -> OutputSignal { diff --git a/esp-hal-common/src/interrupt/riscv.rs b/esp-hal-common/src/interrupt/riscv.rs index d9c4623d418..f1d223f1049 100644 --- a/esp-hal-common/src/interrupt/riscv.rs +++ b/esp-hal-common/src/interrupt/riscv.rs @@ -3,6 +3,8 @@ //! When the `vectored` feature is enabled, CPU interrupts 1 through 15 are //! reserved for each of the possible interrupt priorities. //! +//! On chips with a PLIC CPU interrupts 1,2,5,6,9 .. 19 are used. +//! //! ```rust //! interrupt1() => Priority::Priority1 //! interrupt2() => Priority::Priority2 @@ -141,13 +143,17 @@ pub unsafe fn map(_core: Cpu, interrupt: Interrupt, which: CpuInterrupt) { let interrupt_number = interrupt as isize; let cpu_interrupt_number = which as isize; let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; + #[cfg(not(esp32c6))] let intr_map_base = intr.mac_intr_map.as_ptr(); + #[cfg(esp32c6)] + let intr_map_base = intr.wifi_mac_intr_map.as_ptr(); intr_map_base .offset(interrupt_number) .write_volatile(cpu_interrupt_number as u32); } /// Enable a CPU interrupt +#[cfg(not(any(esp32c6)))] pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) { let cpu_interrupt_number = which as isize; let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; @@ -155,12 +161,27 @@ pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) { .modify(|r, w| w.bits((1 << cpu_interrupt_number) | r.bits())); } +/// Enable a CPU interrupt +#[cfg(esp32c6)] +pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) { + let cpu_interrupt_number = which as isize; + const DR_REG_PLIC_MX_BASE: u32 = 0x20001000; + const PLIC_MXINT_ENABLE_REG: u32 = DR_REG_PLIC_MX_BASE + 0x0; + let mxint_enable = PLIC_MXINT_ENABLE_REG as *mut u32; + unsafe { + mxint_enable.write_volatile(mxint_enable.read_volatile() | 1 << cpu_interrupt_number); + } +} + /// Disable the given peripheral interrupt. pub fn disable(_core: Cpu, interrupt: Interrupt) { unsafe { let interrupt_number = interrupt as isize; let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; + #[cfg(not(esp32c6))] let intr_map_base = intr.mac_intr_map.as_ptr(); + #[cfg(esp32c6)] + let intr_map_base = intr.wifi_mac_intr_map.as_ptr(); intr_map_base.offset(interrupt_number).write_volatile(0); } } @@ -171,7 +192,10 @@ pub fn disable(_core: Cpu, interrupt: Interrupt) { /// interrupt handler will take care of clearing edge interrupt bits. pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) { unsafe { + #[cfg(not(esp32c6))] let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; + #[cfg(esp32c6)] + let intr = &*crate::peripherals::INTPRI::PTR; let cpu_interrupt_number = which as isize; let interrupt_type = match kind { @@ -191,13 +215,32 @@ pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) { /// Great care must be taken when using the `vectored` feature (enabled by /// default). Avoid changing the priority of interrupts 1 - 15 when interrupt /// vectoring is enabled. +#[cfg(not(plic))] pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) { let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; let cpu_interrupt_number = which as isize; let intr_prio_base = intr.cpu_int_pri_0.as_ptr(); intr_prio_base - .offset(cpu_interrupt_number as isize) + .offset(cpu_interrupt_number) + .write_volatile(priority as u32); +} + +/// Set the priority level of an CPU interrupt +/// +/// Great care must be taken when using the `vectored` feature (enabled by +/// default). Avoid changing the priority of interrupts 1 - 15 when interrupt +/// vectoring is enabled. +#[cfg(plic)] +pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) { + const DR_REG_PLIC_MX_BASE: u32 = 0x20001000; + const PLIC_MXINT0_PRI_REG: u32 = DR_REG_PLIC_MX_BASE + 0x10; + + let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32; + + let cpu_interrupt_number = which as isize; + plic_mxint_pri_ptr + .offset(cpu_interrupt_number) .write_volatile(priority as u32); } @@ -206,13 +249,34 @@ pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) pub fn clear(_core: Cpu, which: CpuInterrupt) { unsafe { let cpu_interrupt_number = which as isize; + #[cfg(not(esp32c6))] let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; + #[cfg(esp32c6)] + let intr = &*crate::peripherals::INTPRI::PTR; intr.cpu_int_clear .write(|w| w.bits(1 << cpu_interrupt_number)); } } /// Get status of peripheral interrupts +#[cfg(not(esp32c6))] +#[inline] +pub fn get_status(_core: Cpu) -> u128 { + unsafe { + ((*crate::peripherals::INTERRUPT_CORE0::PTR) + .intr_status_reg_0 + .read() + .bits() as u128) + | ((*crate::peripherals::INTERRUPT_CORE0::PTR) + .intr_status_reg_1 + .read() + .bits() as u128) + << 32 + } +} + +/// Get status of peripheral interrupts +#[cfg(esp32c6)] #[inline] pub fn get_status(_core: Cpu) -> u128 { unsafe { @@ -225,6 +289,11 @@ pub fn get_status(_core: Cpu) -> u128 { .read() .bits() as u128) << 32 + | ((*crate::peripherals::INTERRUPT_CORE0::PTR) + .int_status_reg_2 + .read() + .bits() as u128) + << 64 } } @@ -237,21 +306,39 @@ mod vectored { use super::*; - // Setup interrupts 1-15 ready for vectoring + #[cfg(not(plic))] + const PRIORITY_TO_INTERRUPT: [usize; 15] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + + #[cfg(not(plic))] + const INTERRUPT_TO_PRIORITY: [usize; 15] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + + // don't use interrupts reserved for CLIC (0,3,4,7) + // for some reason also CPU interrupt 8 doesn't work as expected - so don't use + // that, too + #[cfg(plic)] + const PRIORITY_TO_INTERRUPT: [usize; 15] = + [1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]; + + #[cfg(plic)] + const INTERRUPT_TO_PRIORITY: [usize; 19] = [ + 1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + ]; + + // Setup interrupts ready for vectoring #[doc(hidden)] pub(crate) unsafe fn init_vectoring() { - for i in 1..=15 { + for (prio, num) in PRIORITY_TO_INTERRUPT.iter().enumerate() { set_kind( crate::get_core(), - core::mem::transmute(i), + core::mem::transmute(*num as u32), InterruptKind::Level, ); set_priority( crate::get_core(), - core::mem::transmute(i), - core::mem::transmute(i as u8), + core::mem::transmute(*num as u32), + core::mem::transmute((prio as u8) + 1), ); - enable_cpu_interrupt(core::mem::transmute(i)); + enable_cpu_interrupt(core::mem::transmute(*num as u32)); } } @@ -260,8 +347,11 @@ mod vectored { fn get_configured_interrupts(_core: Cpu, mut status: u128) -> [u128; 16] { unsafe { let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; + + #[cfg(not(esp32c6))] let intr_map_base = intr.mac_intr_map.as_ptr(); - let intr_prio_base = intr.cpu_int_pri_0.as_ptr(); + #[cfg(esp32c6)] + let intr_map_base = intr.wifi_mac_intr_map.as_ptr(); let mut prios = [0u128; 16]; @@ -271,9 +361,7 @@ mod vectored { let cpu_interrupt = intr_map_base.offset(i).read_volatile(); // safety: cast is safe because of repr(u32) let cpu_interrupt: CpuInterrupt = core::mem::transmute(cpu_interrupt); - let prio = intr_prio_base - .offset(cpu_interrupt as isize) - .read_volatile(); + let prio = get_priority(cpu_interrupt); prios[prio as usize] |= 1 << i; status &= !(1u128 << interrupt_nr); @@ -283,6 +371,35 @@ mod vectored { } } + /// Get interrupt priority + #[cfg(not(plic))] + #[inline] + unsafe fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority { + let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; + let intr_prio_base = intr.cpu_int_pri_0.as_ptr(); + + let prio = intr_prio_base + .offset(cpu_interrupt as isize) + .read_volatile(); + core::mem::transmute(prio as u8) + } + + /// Get interrupt priority + #[cfg(plic)] + #[inline] + unsafe fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority { + const DR_REG_PLIC_MX_BASE: u32 = 0x20001000; + const PLIC_MXINT0_PRI_REG: u32 = DR_REG_PLIC_MX_BASE + 0x10; + + let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32; + + let cpu_interrupt_number = cpu_interrupt as isize; + let prio = plic_mxint_pri_ptr + .offset(cpu_interrupt_number) + .read_volatile(); + core::mem::transmute(prio as u8) + } + /// Interrupt Error #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Error { @@ -298,7 +415,8 @@ mod vectored { return Err(Error::InvalidInterruptPriority); } unsafe { - let cpu_interrupt = core::mem::transmute(level as u8 as u32); + let cpu_interrupt = + core::mem::transmute(PRIORITY_TO_INTERRUPT[(level as usize) - 1] as u32); map(crate::get_core(), interrupt, cpu_interrupt); enable_cpu_interrupt(cpu_interrupt); } @@ -314,7 +432,8 @@ mod vectored { clear(crate::get_core(), cpu_intr); let configured_interrupts = get_configured_interrupts(crate::get_core(), status); - let mut interrupt_mask = status & configured_interrupts[cpu_intr as usize]; + let mut interrupt_mask = + status & configured_interrupts[INTERRUPT_TO_PRIORITY[cpu_intr as usize - 1]]; while interrupt_mask != 0 { let interrupt_nr = interrupt_mask.trailing_zeros(); // Interrupt::try_from can fail if interrupt already de-asserted: @@ -417,6 +536,30 @@ mod vectored { pub unsafe fn interrupt15(context: &mut TrapFrame) { handle_interrupts(CpuInterrupt::Interrupt15, context) } + #[cfg(plic)] + #[no_mangle] + #[ram] + pub unsafe fn interrupt16(context: &mut TrapFrame) { + handle_interrupts(CpuInterrupt::Interrupt16, context) + } + #[cfg(plic)] + #[no_mangle] + #[ram] + pub unsafe fn interrupt17(context: &mut TrapFrame) { + handle_interrupts(CpuInterrupt::Interrupt17, context) + } + #[cfg(plic)] + #[no_mangle] + #[ram] + pub unsafe fn interrupt18(context: &mut TrapFrame) { + handle_interrupts(CpuInterrupt::Interrupt18, context) + } + #[cfg(plic)] + #[no_mangle] + #[ram] + pub unsafe fn interrupt19(context: &mut TrapFrame) { + handle_interrupts(CpuInterrupt::Interrupt19, context) + } } /// # Safety @@ -579,4 +722,9 @@ pub fn _setup_interrupts() { #[cfg(feature = "vectored")] crate::interrupt::init_vectoring(); }; + + #[cfg(esp32c6)] + unsafe { + core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX); + } } diff --git a/esp-hal-common/src/ledc/channel.rs b/esp-hal-common/src/ledc/channel.rs index 25bff088a14..d98c0cbb14e 100644 --- a/esp-hal-common/src/ledc/channel.rs +++ b/esp-hal-common/src/ledc/channel.rs @@ -32,9 +32,9 @@ pub enum Number { Channel3, Channel4, Channel5, - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] Channel6, - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] Channel7, } @@ -192,7 +192,30 @@ macro_rules! start_duty_without_fading { }; } -#[cfg(not(esp32))] +#[cfg(esp32c6)] +/// Macro to start duty cycle, without fading +macro_rules! start_duty_without_fading { + ($self: ident, $num: literal) => { + paste! { + $self.ledc.[].write(|w| + w.[]() + .set_bit() + ); + $self.ledc.[].write(|w| unsafe { + w.[]() + .set_bit() + .[]() + .bits(0x1) + .[]() + .bits(0x1) + .[]() + .bits(0x0) + }); + } + }; +} + +#[cfg(not(any(esp32, esp32c6)))] /// Macro to start duty cycle, without fading macro_rules! start_duty_without_fading { ($self: ident, $num: literal) => { @@ -399,14 +422,14 @@ where self.output_pin .connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG5); } - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] Number::Channel6 => { set_channel!(self, l, 6, timer_number); update_channel!(self, l, 6); self.output_pin .connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG6); } - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] Number::Channel7 => { set_channel!(self, l, 7, timer_number); update_channel!(self, l, 7); @@ -430,9 +453,9 @@ where Number::Channel3 => set_duty!(self, l, 3, duty), Number::Channel4 => set_duty!(self, l, 4, duty), Number::Channel5 => set_duty!(self, l, 5, duty), - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] Number::Channel6 => set_duty!(self, l, 6, duty), - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] Number::Channel7 => set_duty!(self, l, 7, duty), }; } diff --git a/esp-hal-common/src/ledc/mod.rs b/esp-hal-common/src/ledc/mod.rs index 003971618fa..9a5ffcc27c0 100644 --- a/esp-hal-common/src/ledc/mod.rs +++ b/esp-hal-common/src/ledc/mod.rs @@ -133,9 +133,19 @@ impl<'d> LEDC<'d> { #[cfg(not(esp32))] /// Set global slow clock source pub fn set_global_slow_clock(&mut self, clock_source: LSGlobalClkSource) { + #[cfg(esp32c6)] + let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; + + #[cfg(esp32c6)] + pcr.ledc_sclk_conf.write(|w| w.ledc_sclk_en().set_bit()); + match clock_source { LSGlobalClkSource::APBClk => { - self.ledc.conf.write(|w| unsafe { w.apb_clk_sel().bits(1) }) + #[cfg(not(esp32c6))] + self.ledc.conf.write(|w| unsafe { w.apb_clk_sel().bits(1) }); + #[cfg(esp32c6)] + pcr.ledc_sclk_conf + .write(|w| unsafe { w.ledc_sclk_sel().bits(1) }); } } self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit()); diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 55147e54008..763522ea7ec 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -8,6 +8,7 @@ //! - [esp32-hal] //! - [esp32c2-hal] //! - [esp32c3-hal] +//! - [esp32c6-hal] //! - [esp32s2-hal] //! - [esp32s3-hal] //! @@ -15,6 +16,7 @@ //! [esp32-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32-hal //! [esp32c2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c2-hal //! [esp32c3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c3-hal +//! [esp32c6-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c6-hal //! [esp32s2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s2-hal //! [esp32s3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s3-hal @@ -24,13 +26,6 @@ #![cfg_attr(feature = "async", feature(async_fn_in_trait))] #![cfg_attr(feature = "async", feature(impl_trait_projections))] -#[cfg_attr(esp32, path = "peripherals/esp32.rs")] -#[cfg_attr(esp32c3, path = "peripherals/esp32c3.rs")] -#[cfg_attr(esp32c2, path = "peripherals/esp32c2.rs")] -#[cfg_attr(esp32s2, path = "peripherals/esp32s2.rs")] -#[cfg_attr(esp32s3, path = "peripherals/esp32s3.rs")] -pub mod peripherals; - #[cfg(riscv)] pub use esp_riscv_rt; #[cfg(riscv)] @@ -85,7 +80,7 @@ pub mod ledc; pub mod mcpwm; #[cfg(usb_otg)] pub mod otg_fs; -#[cfg(any(esp32, esp32s2, esp32s3))] +#[cfg(any(esp32, esp32s2, esp32s3, esp32c6))] pub mod pcnt; pub mod peripheral; pub mod prelude; @@ -102,7 +97,7 @@ pub mod system; #[cfg(systimer)] pub mod systimer; pub mod timer; -#[cfg(any(esp32s3, esp32c3))] +#[cfg(any(esp32c3, esp32c6, esp32s3))] pub mod twai; pub mod uart; #[cfg(usb_serial_jtag)] @@ -111,13 +106,14 @@ pub mod usb_serial_jtag; pub mod utils; #[cfg_attr(esp32, path = "cpu_control/esp32.rs")] -#[cfg_attr(any(esp32c2, esp32c3, esp32s2), path = "cpu_control/none.rs")] #[cfg_attr(esp32s3, path = "cpu_control/esp32s3.rs")] +#[cfg_attr(not(any(esp32, esp32s3)), path = "cpu_control/none.rs")] pub mod cpu_control; #[cfg_attr(esp32, path = "efuse/esp32.rs")] #[cfg_attr(esp32c2, path = "efuse/esp32c2.rs")] #[cfg_attr(esp32c3, path = "efuse/esp32c3.rs")] +#[cfg_attr(esp32c6, path = "efuse/esp32c6.rs")] #[cfg_attr(esp32s2, path = "efuse/esp32s2.rs")] #[cfg_attr(esp32s3, path = "efuse/esp32s3.rs")] pub mod efuse; @@ -126,6 +122,23 @@ pub mod efuse; #[cfg_attr(xtensa, path = "interrupt/xtensa.rs")] pub mod interrupt; +#[cfg_attr(esp32, path = "peripherals/esp32.rs")] +#[cfg_attr(esp32c3, path = "peripherals/esp32c3.rs")] +#[cfg_attr(esp32c2, path = "peripherals/esp32c2.rs")] +#[cfg_attr(esp32c6, path = "peripherals/esp32c6.rs")] +#[cfg_attr(esp32s2, path = "peripherals/esp32s2.rs")] +#[cfg_attr(esp32s3, path = "peripherals/esp32s3.rs")] +pub mod peripherals; + +#[cfg(esp32c6)] +pub fn disable_apm_filter() { + unsafe { + (&*esp32c6::LP_APM::PTR).func_ctrl.write(|w| w.bits(0)); + (&*esp32c6::LP_APM0::PTR).func_ctrl.write(|w| w.bits(0)); + (&*esp32c6::HP_APM::PTR).func_ctrl.write(|w| w.bits(0)); + } +} + /// Enumeration of CPU cores /// The actual number of available cores depends on the target. pub enum Cpu { diff --git a/esp-hal-common/src/mcpwm/mod.rs b/esp-hal-common/src/mcpwm/mod.rs index 48dcdd1e889..7cc198da996 100644 --- a/esp-hal-common/src/mcpwm/mod.rs +++ b/esp-hal-common/src/mcpwm/mod.rs @@ -75,6 +75,16 @@ pub mod operator; /// MCPWM timers pub mod timer; +#[cfg(not(esp32c6))] +type RegisterBlock = crate::peripherals::pwm0::RegisterBlock; +#[cfg(esp32c6)] +type RegisterBlock = crate::peripherals::mcpwm::RegisterBlock; + +#[cfg(not(esp32c6))] +type PWM = crate::peripherals::PWM0; +#[cfg(esp32c6)] +type PWM = crate::peripherals::MCPWM; + /// The MCPWM peripheral #[non_exhaustive] pub struct MCPWM<'d, PWM> { @@ -105,12 +115,32 @@ impl<'d, PWM: PwmPeripheral> MCPWM<'d, PWM> { PWM::enable(system); - // set prescaler - peripheral - .clk_cfg - .write(|w| w.clk_prescale().variant(peripheral_clock.prescaler)); - // enable clock - peripheral.clk.write(|w| w.en().set_bit()); + #[cfg(not(esp32c6))] + { + // set prescaler + peripheral + .clk_cfg + .write(|w| w.clk_prescale().variant(peripheral_clock.prescaler)); + + // enable clock + peripheral.clk.write(|w| w.en().set_bit()); + } + + #[cfg(esp32c6)] + { + unsafe { &*crate::peripherals::PCR::PTR } + .pwm_clk_conf + .modify(|_, w| unsafe { + w.pwm_div_num() + .variant(peripheral_clock.prescaler) + .pwm_clkm_en() + .set_bit() + .pwm_clkm_sel() + .bits(1) + }); + + // TODO: Add other clock sources + } MCPWM { _inner: peripheral, @@ -143,6 +173,8 @@ impl<'a> PeripheralClockConfig<'a> { pub fn with_prescaler(clocks: &'a Clocks, prescaler: u8) -> Self { #[cfg(esp32)] let source_clock = clocks.pwm_clock; + #[cfg(esp32c6)] + let source_clock = clocks.crypto_clock; #[cfg(esp32s3)] let source_clock = clocks.crypto_pwm_clock; @@ -173,16 +205,20 @@ impl<'a> PeripheralClockConfig<'a> { ) -> Result { #[cfg(esp32)] let source_clock = clocks.pwm_clock; + #[cfg(esp32c6)] + let source_clock = clocks.crypto_clock; #[cfg(esp32s3)] let source_clock = clocks.crypto_pwm_clock; if target_freq.raw() == 0 || target_freq > source_clock { return Err(FrequencyError); } + let prescaler = source_clock / target_freq - 1; if prescaler > u8::MAX as u32 { return Err(FrequencyError); } + Ok(Self::with_prescaler(clocks, prescaler as u8)) } @@ -237,24 +273,22 @@ impl<'a> PeripheralClockConfig<'a> { pub struct FrequencyError; /// A MCPWM peripheral -pub unsafe trait PwmPeripheral: - Deref -{ +pub unsafe trait PwmPeripheral: Deref { /// Enable peripheral fn enable(system: &mut PeripheralClockControl); /// Get a pointer to the peripheral RegisterBlock - fn block() -> *const crate::peripherals::pwm0::RegisterBlock; + fn block() -> *const RegisterBlock; /// Get operator GPIO mux output signal fn output_signal() -> OutputSignal; } -unsafe impl PwmPeripheral for crate::peripherals::PWM0 { +unsafe impl PwmPeripheral for PWM { fn enable(system: &mut PeripheralClockControl) { system.enable(PeripheralEnable::Mcpwm0) } - fn block() -> *const crate::peripherals::pwm0::RegisterBlock { - Self::ptr() + fn block() -> *const RegisterBlock { + Self::PTR } fn output_signal() -> OutputSignal { @@ -270,13 +304,14 @@ unsafe impl PwmPeripheral for crate::peripherals::PWM0 { } } +#[cfg(not(esp32c6))] unsafe impl PwmPeripheral for crate::peripherals::PWM1 { fn enable(system: &mut PeripheralClockControl) { system.enable(PeripheralEnable::Mcpwm1) } - fn block() -> *const crate::peripherals::pwm0::RegisterBlock { - Self::ptr() + fn block() -> *const RegisterBlock { + Self::PTR } fn output_signal() -> OutputSignal { diff --git a/esp-hal-common/src/mcpwm/operator.rs b/esp-hal-common/src/mcpwm/operator.rs index 5124bd99265..cad3cc01c75 100644 --- a/esp-hal-common/src/mcpwm/operator.rs +++ b/esp-hal-common/src/mcpwm/operator.rs @@ -223,6 +223,38 @@ impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> } } + /// Set how a new timestamp syncs with the timer + #[cfg(esp32c6)] + pub fn set_update_method(&mut self, update_method: PwmUpdateMethod) { + // SAFETY: + // We only write to our GENx_x_UPMETHOD register + let block = unsafe { &*PWM::block() }; + let bits = update_method.0; + match (OP, IS_A) { + (0, true) => block + .gen0_stmp_cfg + .modify(|_, w| w.cmpr0_a_upmethod().variant(bits)), + (1, true) => block + .gen1_stmp_cfg + .modify(|_, w| w.cmpr1_a_upmethod().variant(bits)), + (2, true) => block + .gen2_stmp_cfg + .modify(|_, w| w.cmpr2_a_upmethod().variant(bits)), + (0, false) => block + .gen0_stmp_cfg + .modify(|_, w| w.cmpr0_b_upmethod().variant(bits)), + (1, false) => block + .gen1_stmp_cfg + .modify(|_, w| w.cmpr1_b_upmethod().variant(bits)), + (2, false) => block + .gen2_stmp_cfg + .modify(|_, w| w.cmpr2_b_upmethod().variant(bits)), + _ => { + unreachable!() + } + } + } + /// Set how a new timestamp syncs with the timer. /// The written value will take effect according to the set /// [`PwmUpdateMethod`]. @@ -264,6 +296,27 @@ impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> } } } + + /// Write a new timestamp. + /// The written value will take effect according to the set + /// [`PwmUpdateMethod`]. + #[cfg(esp32c6)] + pub fn set_timestamp(&mut self, value: u16) { + // SAFETY: + // We only write to our GENx_TSTMP_x register + let block = unsafe { &*PWM::block() }; + match (OP, IS_A) { + (0, true) => block.gen0_tstmp_a.write(|w| w.cmpr0_a().variant(value)), + (1, true) => block.gen1_tstmp_a.write(|w| w.cmpr1_a().variant(value)), + (2, true) => block.gen2_tstmp_a.write(|w| w.cmpr2_a().variant(value)), + (0, false) => block.gen0_tstmp_b.write(|w| w.cmpr0_b().variant(value)), + (1, false) => block.gen1_tstmp_b.write(|w| w.cmpr1_b().variant(value)), + (2, false) => block.gen2_tstmp_b.write(|w| w.cmpr2_b().variant(value)), + _ => { + unreachable!() + } + } + } } /// An action the operator applies to an output diff --git a/esp-hal-common/src/mcpwm/timer.rs b/esp-hal-common/src/mcpwm/timer.rs index dbaa5ccbcfe..f104a942ed6 100644 --- a/esp-hal-common/src/mcpwm/timer.rs +++ b/esp-hal-common/src/mcpwm/timer.rs @@ -2,6 +2,10 @@ use core::marker::PhantomData; use fugit::HertzU32; +#[cfg(esp32c6)] +use crate::peripherals::mcpwm::{TIMER0_CFG0, TIMER0_CFG1}; +#[cfg(not(esp32c6))] +use crate::peripherals::pwm0::{TIMER0_CFG0, TIMER0_CFG1}; use crate::{ clock::Clocks, mcpwm::{FrequencyError, PeripheralClockConfig, PwmPeripheral}, @@ -101,9 +105,7 @@ impl Timer { .variant(!sw) }); } - _ => { - unreachable!() - } + _ => unreachable!(), } } @@ -135,13 +137,11 @@ impl Timer { reg.timer2_direction().bit_is_set().into(), ) } - _ => { - unreachable!() - } + _ => unreachable!(), } } - fn cfg0(&mut self) -> &crate::peripherals::pwm0::TIMER0_CFG0 { + fn cfg0(&mut self) -> &TIMER0_CFG0 { // SAFETY: // We only grant access to our CFG0 register with the lifetime of &mut self let block = unsafe { &*PWM::block() }; @@ -153,12 +153,11 @@ impl Timer { 0 => &block.timer0_cfg0, 1 => unsafe { &*(&block.timer1_cfg0 as *const _ as *const _) }, 2 => unsafe { &*(&block.timer2_cfg0 as *const _ as *const _) }, - _ => { - unreachable!() - } + _ => unreachable!(), } } - fn cfg1(&mut self) -> &crate::peripherals::pwm0::TIMER0_CFG1 { + + fn cfg1(&mut self) -> &TIMER0_CFG1 { // SAFETY: // We only grant access to our CFG1 register with the lifetime of &mut self let block = unsafe { &*PWM::block() }; @@ -170,9 +169,7 @@ impl Timer { 0 => &block.timer0_cfg1, 1 => unsafe { &*(&block.timer1_cfg1 as *const _ as *const _) }, 2 => unsafe { &*(&block.timer2_cfg1 as *const _ as *const _) }, - _ => { - unreachable!() - } + _ => unreachable!(), } } } diff --git a/esp-hal-common/src/peripherals/esp32c6.rs b/esp-hal-common/src/peripherals/esp32c6.rs new file mode 100644 index 00000000000..8efc84f3f2d --- /dev/null +++ b/esp-hal-common/src/peripherals/esp32c6.rs @@ -0,0 +1,72 @@ +use esp32c6 as pac; +// We need to export this for users to use +pub use pac::Interrupt; + +// We need to export this in the hal for the drivers to use +pub(crate) use self::peripherals::*; + +crate::peripherals! { + AES => true, + APB_SARADC => true, + ASSIST_DEBUG => true, + ATOMIC => true, + DMA => true, + DS => true, + ECC => true, + EFUSE => true, + EXTMEM => true, + GPIO => true, + GPIOSD => true, + HINF => true, + HMAC => true, + HP_APM => true, + HP_SYS => true, + I2C0 => true, + I2S0 => true, + INTERRUPT_CORE0 => true, + INTPRI => true, + IO_MUX => true, + LEDC => true, + LP_PERI => true, + LP_ANA => true, + LP_AON => true, + LP_APM => true, + LP_APM0 => true, + LP_CLKRST => true, + LP_I2C0 => true, + LP_I2C_ANA_MST => true, + LP_IO => true, + LP_TEE => true, + LP_TIMER => true, + LP_UART => true, + LP_WDT => true, + MCPWM => true, + MEM_MONITOR => true, + OTP_DEBUG => true, + PARL_IO => true, + PAU => true, + PCNT => true, + PCR => true, + PMU => true, + RMT => true, + RNG => true, + RSA => true, + SHA => true, + SLCHOST => true, + SOC_ETM => true, + SPI0 => true, + SPI1 => true, + SPI2 => true, + SYSTIMER => true, + TEE => true, + TIMG0 => true, + TIMG1 => true, + TRACE => true, + TWAI0 => true, + TWAI1 => true, + UART0 => true, + UART1 => true, + UHCI0 => true, + USB_DEVICE => true, + RADIO => false, +} diff --git a/esp-hal-common/src/prelude.rs b/esp-hal-common/src/prelude.rs index ae16750d11a..1e3b0a326c5 100644 --- a/esp-hal-common/src/prelude.rs +++ b/esp-hal-common/src/prelude.rs @@ -20,7 +20,7 @@ pub use fugit::{ }; pub use nb; -#[cfg(any(esp32c2, esp32c3))] +#[cfg(any(esp32c2, esp32c3, esp32c6))] pub use crate::analog::SarAdcExt as _esp_hal_analog_SarAdcExt; #[cfg(any(esp32, esp32s2, esp32s3))] pub use crate::analog::SensExt as _esp_hal_analog_SensExt; diff --git a/esp-hal-common/src/pulse_control.rs b/esp-hal-common/src/pulse_control.rs index 80a6740e5c2..f558dbb788f 100644 --- a/esp-hal-common/src/pulse_control.rs +++ b/esp-hal-common/src/pulse_control.rs @@ -21,6 +21,9 @@ //! * The **ESP32-C3** has 4 channels, `Channel0` and `Channel1` hardcoded for //! transmitting signals and `Channel2` and `Channel3` hardcoded for receiving //! signals. +//! * The **ESP32-C6** has 4 channels, `Channel0` and `Channel1` hardcoded for +//! transmitting signals and `Channel2` and `Channel3` hardcoded for receiving +//! signals. //! * The **ESP32-S2** has 4 channels, each of them can be either receiver or //! transmitter. //! * The **ESP32-S3** has 8 channels, `Channel0`-`Channel3` hardcdoded for @@ -83,6 +86,8 @@ use core::slice::Iter; use fugit::NanosDurationU32; pub use paste::paste; +#[cfg(esp32c6)] +use crate::peripherals::PCR; use crate::{ gpio::{types::OutputSignal, OutputPin}, peripheral::{Peripheral, PeripheralRef}, @@ -124,7 +129,7 @@ pub enum RepeatMode { } /// Specify the clock source for the RMT peripheral -#[cfg(any(esp32c3, esp32s3))] +#[cfg(any(esp32c3, esp32c6, esp32s3))] #[derive(Debug, Copy, Clone)] pub enum ClockSource { /// Application-level clock @@ -150,7 +155,7 @@ pub enum ClockSource { // to the RMT channel #[cfg(any(esp32s2, esp32))] const CHANNEL_RAM_SIZE: u8 = 64; -#[cfg(any(esp32c3, esp32s3))] +#[cfg(any(esp32c3, esp32c6, esp32s3))] const CHANNEL_RAM_SIZE: u8 = 48; // Specifies where the RMT RAM section starts for the particular ESP32 variant @@ -158,6 +163,8 @@ const CHANNEL_RAM_SIZE: u8 = 48; const RMT_RAM_START: usize = 0x3f416400; #[cfg(esp32c3)] const RMT_RAM_START: usize = 0x60016400; +#[cfg(esp32c6)] +const RMT_RAM_START: usize = 0x60006400; #[cfg(esp32)] const RMT_RAM_START: usize = 0x3ff56800; #[cfg(esp32s3)] @@ -283,7 +290,7 @@ macro_rules! channel_instance { let mut channel = $cxi { mem_offset: 0 }; cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32s3))] { // Apply default configuration unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| unsafe { // Configure memory block size @@ -459,7 +466,7 @@ macro_rules! channel_instance { } } - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] conf_reg.modify(|_, w| { // Set config update bit w.conf_update().set_bit() @@ -540,14 +547,14 @@ macro_rules! channel_instance { } // always enable tx wrap - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| { w.mem_tx_wrap_en() .set_bit() }); // apply configuration updates - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| { w.conf_update() .set_bit() @@ -577,9 +584,9 @@ macro_rules! channel_instance { #[cfg(esp32)] false, // The C3/S3 have a slightly different interrupt naming scheme - #[cfg(any(esp32, feature= "esp32s2"))] + #[cfg(any(esp32, esp32s2))] unsafe { interrupts.ch_err_int_raw($num).bit() }, - #[cfg(any(esp32c3, feature= "esp32s3"))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] unsafe { interrupts.ch_tx_err_int_raw($num).bit() }, unsafe { interrupts.ch_tx_thr_event_int_raw($num).bit() }, ) { @@ -612,9 +619,9 @@ macro_rules! channel_instance { #[cfg(esp32)] false, // The C3/S3 have a slightly different interrupt naming scheme - #[cfg(any(esp32, feature= "esp32s2"))] + #[cfg(any(esp32, esp32s2))] unsafe { interrupts.ch_err_int_raw($num).bit() }, - #[cfg(any(esp32c3, feature= "esp32s3"))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] unsafe { interrupts.ch_tx_err_int_raw($num).bit() }, unsafe { interrupts.ch_tx_thr_event_int_raw($num).bit() }, )) @@ -632,7 +639,7 @@ macro_rules! channel_instance { /// previously a sequence was sent with `RepeatMode::Forever`. fn stop_transmission(&self) { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.tx_stop().set_bit()); @@ -666,7 +673,7 @@ macro_rules! output_channel { #[inline(always)] fn set_idle_output_level(&mut self, level: bool) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.idle_out_lv().bit(level)); @@ -683,7 +690,7 @@ macro_rules! output_channel { #[inline(always)] fn set_idle_output(&mut self, state: bool) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.idle_out_en().bit(state)); @@ -700,7 +707,7 @@ macro_rules! output_channel { #[inline(always)] fn set_channel_divider(&mut self, divider: u8) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| unsafe { w.div_cnt().bits(divider) }); @@ -717,7 +724,7 @@ macro_rules! output_channel { #[inline(always)] fn set_carrier_modulation(&mut self, state: bool) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.carrier_en().bit(state)); @@ -845,7 +852,7 @@ macro_rules! rmt { impl<'d> PulseControl<'d> { /// Create a new pulse controller instance - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] pub fn new( instance: impl Peripheral

+ 'd, peripheral_clock_control: &mut PeripheralClockControl, @@ -902,7 +909,7 @@ macro_rules! rmt { /// clock is calculated as follows: /// /// divider = absolute_part + 1 + (fractional_part_a / fractional_part_b) - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] fn config_global( &self, clk_source: ClockSource, @@ -922,7 +929,15 @@ macro_rules! rmt { // addressed! // Configure peripheral - self.reg.sys_conf.modify(|_, w| unsafe { + + #[cfg(esp32c6)] + let pcr = unsafe { &*PCR::ptr() }; + + #[cfg(esp32c6)] + pcr.rmt_sclk_conf.write(|w| w.sclk_en().set_bit()); + + + self.reg.sys_conf.modify(|_, w| // Enable clock w.clk_en() .set_bit() @@ -937,9 +952,23 @@ macro_rules! rmt { .clear_bit() // Disable FIFO mode .apb_fifo_mask() - .set_bit() + .set_bit()); // Select clock source - .sclk_sel() + #[cfg(not(esp32c6))] + self.reg.sys_conf.modify(|_, w| unsafe { + w.sclk_sel() + .bits(clk_source as u8) + // Set absolute part of divider + .sclk_div_num() + .bits(div_abs) + // Set fractional parts of divider to 0 + .sclk_div_a() + .bits(div_frac_a) + .sclk_div_b() + .bits(div_frac_b) }); + #[cfg(esp32c6)] + pcr.rmt_sclk_conf.modify(|_,w| unsafe { + w.sclk_sel() .bits(clk_source as u8) // Set absolute part of divider .sclk_div_num() @@ -1018,7 +1047,7 @@ macro_rules! rmt { }; } -#[cfg(esp32c3)] +#[cfg(any(esp32c3, esp32c6))] rmt!( sys_conf, (0, Channel0, channel0, OutputSignal::RMT_SIG_0), diff --git a/esp-hal-common/src/radio.rs b/esp-hal-common/src/radio.rs index 437b5b4c1ea..61dc63af62a 100644 --- a/esp-hal-common/src/radio.rs +++ b/esp-hal-common/src/radio.rs @@ -38,7 +38,7 @@ impl LowRate { } cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s3, esp32c2, esp32c3))] { + if #[cfg(any(esp32, esp32c2, esp32c3, esp32s3))] { impl RadioExt for crate::peripherals::RADIO { type Components = (Wifi, Bluetooth); @@ -48,6 +48,16 @@ cfg_if::cfg_if! { } } } + } else if #[cfg(esp32c6)] { + impl RadioExt for crate::peripherals::RADIO { + type Components = (Wifi, Bluetooth, LowRate); + + fn split(self) -> Self::Components { + unsafe { + (Wifi::steal(), Bluetooth::steal(), LowRate::steal()) + } + } + } } else if #[cfg(esp32s2)] { impl RadioExt for crate::peripherals::RADIO { type Components = Wifi; diff --git a/esp-hal-common/src/rtc_cntl/mod.rs b/esp-hal-common/src/rtc_cntl/mod.rs index 69ff5e86060..2ec0d77dd97 100644 --- a/esp-hal-common/src/rtc_cntl/mod.rs +++ b/esp-hal-common/src/rtc_cntl/mod.rs @@ -1,29 +1,45 @@ use embedded_hal::watchdog::{Watchdog, WatchdogDisable, WatchdogEnable}; -use fugit::{HertzU32, MicrosDurationU64}; +#[cfg(not(esp32c6))] +use fugit::HertzU32; +use fugit::MicrosDurationU64; use self::rtc::SocResetReason; +#[cfg(not(esp32c6))] +use crate::clock::{Clock, XtalClock}; #[cfg(not(esp32))] use crate::efuse::Efuse; +#[cfg(esp32c6)] +use crate::peripherals::LP_WDT; +#[cfg(not(esp32c6))] +use crate::peripherals::{RTC_CNTL, TIMG0}; use crate::{ - clock::{Clock, XtalClock}, peripheral::{Peripheral, PeripheralRef}, - peripherals::{RTC_CNTL, TIMG0}, Cpu, }; +#[cfg(esp32c6)] +type RtcCntl = crate::peripherals::LP_CLKRST; +#[cfg(not(esp32c6))] +type RtcCntl = crate::peripherals::RTC_CNTL; + #[cfg_attr(esp32, path = "rtc/esp32.rs")] #[cfg_attr(esp32c2, path = "rtc/esp32c2.rs")] #[cfg_attr(esp32c3, path = "rtc/esp32c3.rs")] +#[cfg_attr(esp32c6, path = "rtc/esp32c6.rs")] #[cfg_attr(esp32s2, path = "rtc/esp32s2.rs")] #[cfg_attr(esp32s3, path = "rtc/esp32s3.rs")] mod rtc; +#[cfg(esp32c6)] +pub use rtc::RtcClock; + extern "C" { + #[allow(dead_code)] fn ets_delay_us(us: u32); - fn rtc_get_reset_reason(cpu_num: u32) -> u32; } +#[cfg(not(esp32c6))] #[allow(unused)] #[derive(Debug, Clone, Copy)] /// RTC SLOW_CLK frequency values @@ -34,18 +50,20 @@ pub(crate) enum RtcFastClock { RtcFastClock8m = 1, } +#[cfg(not(esp32c6))] impl Clock for RtcFastClock { fn frequency(&self) -> HertzU32 { match self { RtcFastClock::RtcFastClockXtalD4 => HertzU32::Hz(40_000_000 / 4), #[cfg(any(esp32, esp32s2))] RtcFastClock::RtcFastClock8m => HertzU32::Hz(8_500_000), - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] RtcFastClock::RtcFastClock8m => HertzU32::Hz(17_500_000), } } } +#[cfg(not(esp32c6))] #[allow(unused)] #[derive(Debug, Clone, Copy)] /// RTC SLOW_CLK frequency values @@ -58,6 +76,7 @@ pub(crate) enum RtcSlowClock { RtcSlowClock8mD256 = 2, } +#[cfg(not(esp32c6))] impl Clock for RtcSlowClock { fn frequency(&self) -> HertzU32 { match self { @@ -65,18 +84,24 @@ impl Clock for RtcSlowClock { RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(150_000), #[cfg(esp32s2)] RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(90_000), - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(136_000), - RtcSlowClock::RtcSlowClock32kXtal => HertzU32::Hz(32768), + RtcSlowClock::RtcSlowClock32kXtal => HertzU32::Hz(32_768), #[cfg(any(esp32, esp32s2))] RtcSlowClock::RtcSlowClock8mD256 => HertzU32::Hz(8_500_000 / 256), #[cfg(any(esp32c2, esp32c3, esp32s3))] RtcSlowClock::RtcSlowClock8mD256 => HertzU32::Hz(17_500_000 / 256), + #[cfg(esp32c6)] + RtcSlowClock::RtcSlowClock32kRc => HertzU32::Hz(32_768), + #[cfg(esp32c6)] + RtcSlowClock::RtcCalInternalOsc => HertzU32::Hz(32_768), + #[cfg(esp32c6)] + RtcSlowClock::RtcCalRcFast => HertzU32::Hz(150_000), } } } -#[allow(unused)] +#[cfg(not(esp32c6))] #[derive(Debug, Clone, Copy)] /// Clock source to be calibrated using rtc_clk_cal function pub(crate) enum RtcCalSel { @@ -92,33 +117,37 @@ pub(crate) enum RtcCalSel { } pub struct Rtc<'d> { - _inner: PeripheralRef<'d, RTC_CNTL>, + _inner: PeripheralRef<'d, RtcCntl>, pub rwdt: Rwdt, - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] pub swd: Swd, } impl<'d> Rtc<'d> { - pub fn new(rtc_cntl: impl Peripheral

+ 'd) -> Self { + pub fn new(rtc_cntl: impl Peripheral

+ 'd) -> Self { rtc::init(); rtc::configure_clock(); Self { _inner: rtc_cntl.into_ref(), rwdt: Rwdt::default(), - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] swd: Swd::new(), } } + // TODO: implement for ESP32-C6 + #[cfg(not(esp32c6))] pub fn estimate_xtal_frequency(&mut self) -> u32 { RtcClock::estimate_xtal_frequency() } } +#[cfg(not(esp32c6))] /// RTC Watchdog Timer pub struct RtcClock; +#[cfg(not(esp32c6))] /// RTC Watchdog Timer driver impl RtcClock { const CAL_FRACT: u32 = 19; @@ -135,8 +164,9 @@ impl RtcClock { /// /// When 8MHz/256 divided output is not needed, the divider should be /// disabled to reduce power consumption. + #[cfg(not(esp32c6))] fn enable_8m(clk_8m_en: bool, d256_en: bool) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; if clk_8m_en { rtc_cntl.clk_conf.modify(|_, w| w.enb_ck8m().clear_bit()); @@ -160,12 +190,29 @@ impl RtcClock { } } + #[cfg(esp32c6)] + fn enable_8m(clk_8m_en: bool, _d256_en: bool) { + let pmu = unsafe { &*PMU::PTR }; + + if clk_8m_en { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit()); + + unsafe { ets_delay_us(50) }; + } else { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_fosc_clk().clear_bit()); + } + } + /// Get main XTAL frequency /// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the /// bootloader, as passed to rtc_clk_init function. fn get_xtal_freq() -> XtalClock { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; - let xtal_freq_reg = rtc_cntl.store4.read().bits(); + #[cfg(esp32c6)] + let xtal_freq_reg = unsafe { &*LP_AON::PTR }.store4.read().bits(); + #[cfg(not(esp32c6))] + let xtal_freq_reg = unsafe { &*RTC_CNTL::PTR }.store4.read().bits(); // Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in // lower and upper 16-bit halves. These are the routines to work with such a @@ -192,8 +239,9 @@ impl RtcClock { } /// Get the RTC_SLOW_CLK source + #[cfg(not(esp32c6))] fn get_slow_freq() -> RtcSlowClock { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; let slow_freq = rtc_cntl.clk_conf.read().ana_clk_rtc_sel().bits(); match slow_freq { 0 => RtcSlowClock::RtcSlowClockRtc, @@ -203,10 +251,26 @@ impl RtcClock { } } + /// Get the RTC_SLOW_CLK source + #[cfg(esp32c6)] + fn get_slow_freq() -> RtcSlowClock { + let lp_clrst = unsafe { &*LP_CLKRST::ptr() }; + + let slow_freq = lp_clrst.lp_clk_conf.read().slow_clk_sel().bits(); + match slow_freq { + 0 => RtcSlowClock::RtcSlowClockRtc, + 1 => RtcSlowClock::RtcSlowClock32kXtal, + 2 => RtcSlowClock::RtcSlowClock32kRc, + 3 => RtcSlowClock::RtcCalInternalOsc, + _ => unreachable!(), + } + } + /// Select source for RTC_SLOW_CLK + #[cfg(not(esp32c6))] fn set_slow_freq(slow_freq: RtcSlowClock) { unsafe { - let rtc_cntl = &*RTC_CNTL::ptr(); + let rtc_cntl = &*RTC_CNTL::PTR; rtc_cntl.clk_conf.modify(|_, w| { w.ana_clk_rtc_sel() .bits(slow_freq as u8) @@ -231,10 +295,16 @@ impl RtcClock { }; } + #[cfg(esp32c6)] + fn set_slow_freq(slow_freq: RtcSlowClock) { + todo!() + } + /// Select source for RTC_FAST_CLK + #[cfg(not(esp32c6))] fn set_fast_freq(fast_freq: RtcFastClock) { unsafe { - let rtc_cntl = &*RTC_CNTL::ptr(); + let rtc_cntl = &*RTC_CNTL::PTR; rtc_cntl.clk_conf.modify(|_, w| { w.fast_clk_rtc_sel().bit(match fast_freq { RtcFastClock::RtcFastClock8m => true, @@ -246,9 +316,15 @@ impl RtcClock { }; } + #[cfg(esp32c6)] + fn set_fast_freq(fast_freq: RtcFastClock) { + todo!() + } + /// Calibration of RTC_SLOW_CLK is performed using a special feature of /// TIMG0. This feature counts the number of XTAL clock cycles within a /// given number of RTC_SLOW_CLK cycles. + #[cfg(not(esp32c6))] fn calibrate_internal(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { // Except for ESP32, choosing RTC_CAL_RTC_MUX results in calibration of // the 150k RTC clock (90k on ESP32-S2) regardless of the currently selected @@ -264,8 +340,8 @@ impl RtcClock { RtcCalSel::RtcCalInternalOsc => RtcCalSel::RtcCalRtcMux, _ => cal_clk, }; - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; - let timg0 = unsafe { &*TIMG0::ptr() }; + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + let timg0 = unsafe { &*TIMG0::PTR }; // Enable requested clock (150k clock is always on) let dig_32k_xtal_enabled = rtc_cntl.clk_conf.read().dig_xtal32k_en().bit_is_set(); @@ -393,6 +469,14 @@ impl RtcClock { cal_val } + /// Calibration of RTC_SLOW_CLK is performed using a special feature of + /// TIMG0. This feature counts the number of XTAL clock cycles within a + /// given number of RTC_SLOW_CLK cycles. + #[cfg(esp32c6)] + fn calibrate_internal(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { + todo!() + } + /// Measure ratio between XTAL frequency and RTC slow clock frequency fn get_calibration_ratio(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64; @@ -423,7 +507,14 @@ impl RtcClock { match RtcClock::get_slow_freq() { RtcSlowClock::RtcSlowClockRtc => RtcCalSel::RtcCalRtcMux, RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, + #[cfg(not(esp32c6))] RtcSlowClock::RtcSlowClock8mD256 => RtcCalSel::RtcCal8mD256, + #[cfg(esp32c6)] + RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, + #[cfg(esp32c6)] + RtcSlowClock::RtcCalInternalOsc => RtcCalSel::RtcCalInternalOsc, + #[cfg(esp32c6)] + RtcSlowClock::RtcCalRcFast => RtcCalSel::RtcCalRcFast, }, 1024, ); @@ -434,11 +525,13 @@ impl RtcClock { (100_000_000 * 1000 / period) as u16 } + // TODO: implement for ESP32-C6 + #[cfg(not(esp32c6))] fn estimate_xtal_frequency() -> u32 { // Number of 8M/256 clock cycles to use for XTAL frequency estimation. const XTAL_FREQ_EST_CYCLES: u32 = 10; - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; let clk_8m_enabled = rtc_cntl.clk_conf.read().enb_ck8m().bit_is_clear(); let clk_8md256_enabled = rtc_cntl.clk_conf.read().enb_ck8m_div().bit_is_clear(); @@ -490,7 +583,10 @@ impl Default for Rwdt { /// RTC Watchdog Timer driver impl Rwdt { pub fn listen(&mut self) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; self.stg0_action = RwdtStageAction::RwdtStageActionInterrupt; @@ -512,7 +608,10 @@ impl Rwdt { } pub fn unlisten(&mut self) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; self.stg0_action = RwdtStageAction::RwdtStageActionResetRtc; @@ -534,7 +633,10 @@ impl Rwdt { } pub fn clear_interrupt(&mut self) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; self.set_write_protection(false); @@ -547,7 +649,10 @@ impl Rwdt { } pub fn is_interrupt_set(&self) -> bool { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; cfg_if::cfg_if! { if #[cfg(esp32)] { @@ -560,7 +665,10 @@ impl Rwdt { /// Enable/disable write protection for WDT registers fn set_write_protection(&mut self, enable: bool) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; let wkey = if enable { 0u32 } else { 0x50D8_3AA1 }; rtc_cntl.wdtwprotect.write(|w| unsafe { w.bits(wkey) }); @@ -569,7 +677,10 @@ impl Rwdt { impl WatchdogDisable for Rwdt { fn disable(&mut self) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; self.set_write_protection(false); @@ -581,6 +692,7 @@ impl WatchdogDisable for Rwdt { } } +// TODO: this can be refactored impl WatchdogEnable for Rwdt { type Time = MicrosDurationU64; @@ -588,9 +700,12 @@ impl WatchdogEnable for Rwdt { where T: Into, { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; - let timeout_raw = (period.into().to_millis() * (RtcClock::cycles_to_1ms() as u64)) as u32; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; + let timeout_raw = (period.into().to_millis() * (RtcClock::cycles_to_1ms() as u64)) as u32; self.set_write_protection(false); unsafe { @@ -599,7 +714,13 @@ impl WatchdogEnable for Rwdt { .wdtconfig1 .modify(|_, w| w.wdt_stg0_hold().bits(timeout_raw)); - #[cfg(not(esp32))] + #[cfg(esp32c6)] + (&*LP_WDT::PTR).config1.modify(|_, w| { + w.wdt_stg0_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }); + + #[cfg(not(any(esp32, esp32c6)))] rtc_cntl.wdtconfig1.modify(|_, w| { w.wdt_stg0_hold() .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) @@ -629,21 +750,22 @@ impl WatchdogEnable for Rwdt { impl Watchdog for Rwdt { fn feed(&mut self) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; self.set_write_protection(false); - rtc_cntl.wdtfeed.write(|w| unsafe { w.bits(1) }); - self.set_write_protection(true); } } -#[cfg(any(esp32c2, esp32c3, esp32s3))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] /// Super Watchdog pub struct Swd; -#[cfg(any(esp32c2, esp32c3, esp32s3))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] /// Super Watchdog driver impl Swd { pub fn new() -> Self { @@ -652,8 +774,15 @@ impl Swd { /// Enable/disable write protection for WDT registers fn set_write_protection(&mut self, enable: bool) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; + + #[cfg(not(esp32c6))] let wkey = if enable { 0u32 } else { 0x8F1D_312A }; + #[cfg(esp32c6)] + let wkey = if enable { 0u32 } else { 0x50D8_3AA1 }; rtc_cntl .swd_wprotect @@ -661,15 +790,16 @@ impl Swd { } } -#[cfg(any(esp32c2, esp32c3, esp32s3))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] impl WatchdogDisable for Swd { fn disable(&mut self) { - let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; + #[cfg(not(esp32c6))] + let rtc_cntl = unsafe { &*RTC_CNTL::PTR }; + #[cfg(esp32c6)] + let rtc_cntl = unsafe { &*LP_WDT::PTR }; self.set_write_protection(false); - rtc_cntl.swd_conf.write(|w| w.swd_auto_feed_en().set_bit()); - self.set_write_protection(true); } } diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32c6.rs b/esp-hal-common/src/rtc_cntl/rtc/esp32c6.rs new file mode 100644 index 00000000000..c26d91de854 --- /dev/null +++ b/esp-hal-common/src/rtc_cntl/rtc/esp32c6.rs @@ -0,0 +1,672 @@ +use fugit::HertzU32; +use strum::FromRepr; + +use crate::{ + clock::{clocks_ll::regi2c_write_mask, Clock, XtalClock}, + peripherals::{LP_AON, LP_CLKRST, PCR, PMU, TIMG0}, +}; + +const I2C_DIG_REG: u8 = 0x6d; +const I2C_DIG_REG_HOSTID: u8 = 0; + +const I2C_DIG_REG_XPD_RTC_REG: u8 = 13; +const I2C_DIG_REG_XPD_RTC_REG_MSB: u8 = 2; +const I2C_DIG_REG_XPD_RTC_REG_LSB: u8 = 2; + +const I2C_DIG_REG_XPD_DIG_REG: u8 = 13; +const I2C_DIG_REG_XPD_DIG_REG_MSB: u8 = 3; +const I2C_DIG_REG_XPD_DIG_REG_LSB: u8 = 3; + +const I2C_DIG_REG_ENIF_RTC_DREG: u8 = 5; +const I2C_DIG_REG_ENIF_RTC_DREG_MSB: u8 = 7; +const I2C_DIG_REG_ENIF_RTC_DREG_LSB: u8 = 7; + +const I2C_DIG_REG_ENIF_DIG_DREG: u8 = 7; +const I2C_DIG_REG_ENIF_DIG_DREG_MSB: u8 = 7; +const I2C_DIG_REG_ENIF_DIG_DREG_LSB: u8 = 7; + +const I2C_DIG_REG_SCK_DCAP: u8 = 14; +const I2C_DIG_REG_SCK_DCAP_MSB: u8 = 7; +const I2C_DIG_REG_SCK_DCAP_LSB: u8 = 0; + +pub(crate) fn init() { + let pmu = unsafe { &*PMU::ptr() }; + + pmu.rf_pwc + .modify(|_, w| w.perif_i2c_rstb().set_bit().xpd_perif_i2c().set_bit()); + + unsafe { + regi2c_write_mask( + I2C_DIG_REG, + I2C_DIG_REG_HOSTID, + I2C_DIG_REG_ENIF_RTC_DREG, + I2C_DIG_REG_ENIF_RTC_DREG_MSB, + I2C_DIG_REG_ENIF_RTC_DREG_LSB, + 1, + ); + regi2c_write_mask( + I2C_DIG_REG, + I2C_DIG_REG_HOSTID, + I2C_DIG_REG_ENIF_DIG_DREG, + I2C_DIG_REG_ENIF_DIG_DREG_MSB, + I2C_DIG_REG_ENIF_DIG_DREG_LSB, + 1, + ); + + regi2c_write_mask( + I2C_DIG_REG, + I2C_DIG_REG_HOSTID, + I2C_DIG_REG_XPD_RTC_REG, + I2C_DIG_REG_XPD_RTC_REG_MSB, + I2C_DIG_REG_XPD_RTC_REG_LSB, + 0, + ); + regi2c_write_mask( + I2C_DIG_REG, + I2C_DIG_REG_HOSTID, + I2C_DIG_REG_XPD_DIG_REG, + I2C_DIG_REG_XPD_DIG_REG_MSB, + I2C_DIG_REG_XPD_DIG_REG_LSB, + 0, + ); + + pmu.hp_active_hp_regulator0 + .modify(|_, w| w.hp_active_hp_regulator_dbias().bits(25)); + pmu.hp_sleep_lp_regulator0 + .modify(|_, w| w.hp_sleep_lp_regulator_dbias().bits(26)); + } +} + +pub(crate) fn configure_clock() { + assert!(matches!( + RtcClock::get_xtal_freq(), + XtalClock::RtcXtalFreq40M + )); + + RtcClock::set_fast_freq(RtcFastClock::RtcFastClockRcFast); + + let cal_val = loop { + RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRcSlow); + + let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024); + if res != 0 { + break res; + } + }; + + unsafe { + let lp_aon = &*LP_AON::ptr(); + lp_aon.store1.modify(|_, w| w.bits(cal_val)); + } + + modem_clk_domain_active_state_icg_map_preinit(); +} + +fn modem_clk_domain_active_state_icg_map_preinit() { + unsafe { + let pmu = &*PMU::PTR; + let lp_clkrst = &*LP_CLKRST::PTR; + + pmu.hp_active_icg_modem + .modify(|_, w| w.hp_active_dig_icg_modem_code().bits(2)); + + const MODEM_SYSCON_CLK_CONF_POWER_ST: u32 = 0x600A9800 + 0xc; + const MODEM_LPCON_CLK_CONF_POWER_ST: u32 = 0x600A9800 + 0x20; + + (MODEM_SYSCON_CLK_CONF_POWER_ST as *mut u32).write_volatile( + (MODEM_SYSCON_CLK_CONF_POWER_ST as *mut u32).read_volatile() & !(3 << 28) | 2 << 28, + ); + + (MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).write_volatile( + (MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).read_volatile() & !(3 << 28) | 2 << 28, + ); + + (MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).write_volatile( + (MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).read_volatile() & !(3 << 28) | 2 << 28, + ); + + pmu.imm_modem_icg + .write(|w| w.update_dig_icg_modem_en().set_bit()); + pmu.imm_sleep_sysclk + .write(|w| w.update_dig_icg_switch().set_bit()); + + lp_clkrst.fosc_cntl.modify(|_, w| w.fosc_dfreq().bits(100)); + regi2c_write_mask( + I2C_DIG_REG, + I2C_DIG_REG_HOSTID, + I2C_DIG_REG_SCK_DCAP, + I2C_DIG_REG_SCK_DCAP_MSB, + I2C_DIG_REG_SCK_DCAP_LSB, + 128, + ); + lp_clkrst + .rc32k_cntl + .modify(|_, w| w.rc32k_dfreq().bits(100)); + } +} + +// Terminology: +// +// CPU Reset: Reset CPU core only, once reset done, CPU will execute from +// reset vector +// Core Reset: Reset the whole digital system except RTC sub-system +// System Reset: Reset the whole digital system, including RTC sub-system +// Chip Reset: Reset the whole chip, including the analog part + +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] +pub enum SocResetReason { + /// Power on reset + /// + /// In ESP-IDF this value (0x01) can *also* be `ChipBrownOut` or + /// `ChipSuperWdt`, however that is not really compatible with Rust-style + /// enums. + ChipPowerOn = 0x01, + /// Software resets the digital core by RTC_CNTL_SW_SYS_RST + CoreSw = 0x03, + /// Deep sleep reset the digital core + CoreDeepSleep = 0x05, + /// SDIO Core reset + CoreSDIO = 0x06, + /// Main watch dog 0 resets digital core + CoreMwdt0 = 0x07, + /// Main watch dog 1 resets digital core + CoreMwdt1 = 0x08, + /// RTC watch dog resets digital core + CoreRtcWdt = 0x09, + /// Main watch dog 0 resets CPU 0 + Cpu0Mwdt0 = 0x0B, + /// Software resets CPU 0 by RTC_CNTL_SW_PROCPU_RST + Cpu0Sw = 0x0C, + /// RTC watch dog resets CPU 0 + Cpu0RtcWdt = 0x0D, + /// VDD voltage is not stable and resets the digital core + SysBrownOut = 0x0F, + /// RTC watch dog resets digital core and rtc module + SysRtcWdt = 0x10, + /// Main watch dog 1 resets CPU 0 + Cpu0Mwdt1 = 0x11, + /// Super watch dog resets the digital core and rtc module + SysSuperWdt = 0x12, + /// eFuse CRC error resets the digital core + CoreEfuseCrc = 0x14, + /// USB UART resets the digital core + CoreUsbUart = 0x15, + /// USB JTAG resets the digital core + CoreUsbJtag = 0x16, + /// JTAG resets CPU + Cpu0JtagCpu = 0x18, +} + +extern "C" { + fn ets_delay_us(us: u32); +} + +#[allow(unused)] +#[derive(Debug, Clone, Copy)] +/// RTC SLOW_CLK frequency values +pub(crate) enum RtcFastClock { + /// Select RC_FAST_CLK as RTC_FAST_CLK source + RtcFastClockRcFast = 0, + /// Select XTAL_D2_CLK as RTC_FAST_CLK source + RtcFastClockXtalD2 = 1, +} + +impl Clock for RtcFastClock { + fn frequency(&self) -> HertzU32 { + match self { + RtcFastClock::RtcFastClockXtalD2 => HertzU32::Hz(40_000_000 / 2), /* TODO: Is the value correct? */ + RtcFastClock::RtcFastClockRcFast => HertzU32::Hz(17_500_000), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +/// RTC SLOW_CLK frequency values +pub(crate) enum RtcSlowClock { + /// Select RC_SLOW_CLK as RTC_SLOW_CLK source + RtcSlowClockRcSlow = 0, + /// Select XTAL32K_CLK as RTC_SLOW_CLK source + RtcSlowClock32kXtal = 1, + /// Select RC32K_CLK as RTC_SLOW_CLK source + RtcSlowClock32kRc = 2, + /// Select OSC_SLOW_CLK (external slow clock) as RTC_SLOW_CLK source + RtcSlowOscSlow = 3, +} + +impl Clock for RtcSlowClock { + fn frequency(&self) -> HertzU32 { + match self { + RtcSlowClock::RtcSlowClockRcSlow => HertzU32::Hz(136_000), + RtcSlowClock::RtcSlowClock32kXtal => HertzU32::Hz(32_768), + RtcSlowClock::RtcSlowClock32kRc => HertzU32::Hz(32_768), + RtcSlowClock::RtcSlowOscSlow => HertzU32::Hz(32_768), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +/// Clock source to be calibrated using rtc_clk_cal function +pub(crate) enum RtcCalSel { + /// Currently selected RTC SLOW_CLK + RtcCalRtcMux = -1, + /// Internal 150kHz RC oscillator + RtcCalRcSlow = 0, + /// External 32kHz XTAL, as one type of 32k clock + RtcCal32kXtal = 1, + /// Internal 32kHz RC oscillator, as one type of 32k clock + RtcCal32kRc = 2, + /// External slow clock signal input by lp_pad_gpio0, as one type of 32k + /// clock + RtcCal32kOscSlow = 3, + /// Internal 20MHz RC oscillator + RtcCalRcFast, +} + +#[derive(Clone)] +pub(crate) enum RtcCaliClkSel { + CaliClkRcSlow = 0, + CaliClkRcFast = 1, + CaliClk32k = 2, +} +/// RTC Watchdog Timer +pub struct RtcClock; + +/// RTC Watchdog Timer driver +impl RtcClock { + const CAL_FRACT: u32 = 19; + + /// Enable or disable 8 MHz internal oscillator + fn enable_8m(clk_8m_en: bool, _d256_en: bool) { + let pmu = unsafe { &*PMU::PTR }; + + if clk_8m_en { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit()); + + unsafe { ets_delay_us(50) }; + } else { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_fosc_clk().clear_bit()); + } + } + + /// Get main XTAL frequency + /// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the + /// bootloader, as passed to rtc_clk_init function. + fn get_xtal_freq() -> XtalClock { + let xtal_freq_reg = unsafe { &*LP_AON::PTR }.store4.read().bits(); + + // Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in + // lower and upper 16-bit halves. These are the routines to work with such a + // representation. + let clk_val_is_valid = |val| { + (val & 0xffffu32) == ((val >> 16u32) & 0xffffu32) && val != 0u32 && val != u32::MAX + }; + let reg_val_to_clk_val = |val| val & u16::MAX as u32; + + if !clk_val_is_valid(xtal_freq_reg) { + return XtalClock::RtcXtalFreq40M; + } + + match reg_val_to_clk_val(xtal_freq_reg) { + 40 => XtalClock::RtcXtalFreq40M, + other => XtalClock::RtcXtalFreqOther(other), + } + } + + /// Get the RTC_SLOW_CLK source + fn get_slow_freq() -> RtcSlowClock { + let lp_clrst = unsafe { &*LP_CLKRST::ptr() }; + + let slow_freq = lp_clrst.lp_clk_conf.read().slow_clk_sel().bits(); + match slow_freq { + 0 => RtcSlowClock::RtcSlowClockRcSlow, + 1 => RtcSlowClock::RtcSlowClock32kXtal, + 2 => RtcSlowClock::RtcSlowClock32kRc, + 3 => RtcSlowClock::RtcSlowOscSlow, + _ => unreachable!(), + } + } + + fn set_slow_freq(slow_freq: RtcSlowClock) { + unsafe { + let lp_clkrst = &*LP_CLKRST::PTR; + + lp_clkrst + .lp_clk_conf + .modify(|_, w| w.slow_clk_sel().bits(slow_freq as u8)); + lp_clkrst.clk_to_hp.modify(|_, w| { + w.icg_hp_xtal32k() + .bit(match slow_freq { + RtcSlowClock::RtcSlowClock32kXtal => true, + _ => false, + }) + .icg_hp_xtal32k() + .bit(match slow_freq { + RtcSlowClock::RtcSlowClock32kXtal => true, + _ => false, + }) + }); + } + } + + // TODO: IDF-5781 Some of esp32c6 SOC_RTC_FAST_CLK_SRC_XTAL_D2 rtc_fast clock + // has timing issue Force to use SOC_RTC_FAST_CLK_SRC_RC_FAST since 2nd + // stage bootloader https://github.com/espressif/esp-idf/blob/master/components/bootloader_support/src/bootloader_clock_init.c#L65-L67 + fn set_fast_freq(fast_freq: RtcFastClock) { + unsafe { + let lp_clkrst = &*LP_CLKRST::PTR; + lp_clkrst.lp_clk_conf.modify(|_, w| { + w.fast_clk_sel().bit(match fast_freq { + RtcFastClock::RtcFastClockRcFast => false, + RtcFastClock::RtcFastClockXtalD2 => true, + }) + }); + ets_delay_us(3); + } + } + + /// Calibration of RTC_SLOW_CLK is performed using a special feature of + /// TIMG0. This feature counts the number of XTAL clock cycles within a + /// given number of RTC_SLOW_CLK cycles. + fn calibrate_internal(mut cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { + const SOC_CLK_RC_FAST_FREQ_APPROX: u32 = 17_500_000; + const SOC_CLK_RC_SLOW_FREQ_APPROX: u32 = 136_000; + const SOC_CLK_XTAL32K_FREQ_APPROX: u32 = 32768; + + if cal_clk == RtcCalSel::RtcCalRtcMux { + cal_clk = match cal_clk { + RtcCalSel::RtcCalRtcMux => match RtcClock::get_slow_freq() { + RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, + RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, + _ => cal_clk, + }, + RtcCalSel::RtcCal32kOscSlow => RtcCalSel::RtcCalRtcMux, + _ => cal_clk, + }; + } + + let lp_clkrst = unsafe { &*LP_CLKRST::ptr() }; + let pcr = unsafe { &*PCR::ptr() }; + let pmu = unsafe { &*PMU::ptr() }; + + let clk_src = RtcClock::get_slow_freq(); + + if cal_clk == RtcCalSel::RtcCalRtcMux { + cal_clk = match clk_src { + RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRcSlow, + RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, + RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, + RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCal32kOscSlow, + }; + } + + let cali_clk_sel; + if cal_clk == RtcCalSel::RtcCalRtcMux { + cal_clk = match clk_src { + RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRcSlow, + RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, + RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, + RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCalRcSlow, + } + } + + if cal_clk == RtcCalSel::RtcCalRcFast { + cali_clk_sel = RtcCaliClkSel::CaliClkRcFast; + } else if cal_clk == RtcCalSel::RtcCalRcSlow { + cali_clk_sel = RtcCaliClkSel::CaliClkRcSlow; + } else { + cali_clk_sel = RtcCaliClkSel::CaliClk32k; + match cal_clk { + RtcCalSel::RtcCalRtcMux | RtcCalSel::RtcCalRcSlow | RtcCalSel::RtcCalRcFast => (), + RtcCalSel::RtcCal32kRc => pcr + .ctrl_32k_conf + .modify(|_, w| unsafe { w.clk_32k_sel().bits(0) }), + RtcCalSel::RtcCal32kXtal => pcr + .ctrl_32k_conf + .modify(|_, w| unsafe { w.clk_32k_sel().bits(1) }), + RtcCalSel::RtcCal32kOscSlow => pcr + .ctrl_32k_conf + .modify(|_, w| unsafe { w.clk_32k_sel().bits(2) }), + } + } + + // Enable requested clock (150k is always on) + // Some delay is required before the time is stable + // Only enable if originaly was disabled + // If clock is already on, do nothing + + let dig_32k_xtal_enabled = lp_clkrst.clk_to_hp.read().icg_hp_xtal32k().bit_is_set(); + + if cal_clk == RtcCalSel::RtcCal32kXtal && !dig_32k_xtal_enabled { + lp_clkrst + .clk_to_hp + .modify(|_, w| w.icg_hp_xtal32k().set_bit()); + } + + // TODO: very hacky + // in ESP-IDF these are not called in this function but the fields are set + lp_clkrst + .clk_to_hp + .modify(|_, w| w.icg_hp_xtal32k().set_bit()); + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_xtal32k().set_bit()); + + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit()); + + let rc_fast_enabled = pmu + .hp_sleep_lp_ck_power + .read() + .hp_sleep_xpd_fosc_clk() + .bit_is_set(); + let dig_rc_fast_enabled = lp_clkrst.clk_to_hp.read().icg_hp_fosc().bit_is_set(); + + if cal_clk == RtcCalSel::RtcCalRcFast { + if !rc_fast_enabled { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit()); + unsafe { + ets_delay_us(50); + } + } + + if !dig_rc_fast_enabled { + lp_clkrst.clk_to_hp.modify(|_, w| w.icg_hp_fosc().set_bit()); + unsafe { + ets_delay_us(5); + } + } + } + + let rc32k_enabled = pmu + .hp_sleep_lp_ck_power + .read() + .hp_sleep_xpd_rc32k() + .bit_is_set(); + let dig_rc32k_enabled = lp_clkrst.clk_to_hp.read().icg_hp_osc32k().bit_is_set(); + + if cal_clk == RtcCalSel::RtcCal32kRc { + if !rc32k_enabled { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit()); + unsafe { + ets_delay_us(300); + } + } + + if !dig_rc32k_enabled { + lp_clkrst + .clk_to_hp + .modify(|_, w| w.icg_hp_osc32k().set_bit()); + } + } + + // Check if there is already running calibration process + // TODO: &mut TIMG0 for calibration + let timg0 = unsafe { &*TIMG0::ptr() }; + + if timg0 + .rtccalicfg + .read() + .rtc_cali_start_cycling() + .bit_is_set() + { + timg0 + .rtccalicfg2 + .modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(1) }); + + // Set small timeout threshold to accelerate the generation of timeot + // Internal circuit will be reset when timeout occurs and will not affect the + // next calibration + while !timg0.rtccalicfg.read().rtc_cali_rdy().bit_is_set() + && !timg0.rtccalicfg2.read().rtc_cali_timeout().bit_is_set() + {} + } + + // Prepare calibration + timg0 + .rtccalicfg + .modify(|_, w| unsafe { w.rtc_cali_clk_sel().bits(cali_clk_sel.clone() as u8) }); + timg0 + .rtccalicfg + .modify(|_, w| w.rtc_cali_start_cycling().clear_bit()); + timg0 + .rtccalicfg + .modify(|_, w| unsafe { w.rtc_cali_max().bits(slowclk_cycles as u16) }); + + let expected_freq = match cali_clk_sel { + RtcCaliClkSel::CaliClk32k => { + timg0.rtccalicfg2.modify(|_, w| unsafe { + w.rtc_cali_timeout_thres().bits(slowclk_cycles << 12) + }); + SOC_CLK_XTAL32K_FREQ_APPROX + } + RtcCaliClkSel::CaliClkRcFast => { + timg0 + .rtccalicfg2 + .modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(0x01FFFFFF) }); + SOC_CLK_RC_FAST_FREQ_APPROX + } + _ => { + timg0.rtccalicfg2.modify(|_, w| unsafe { + w.rtc_cali_timeout_thres().bits(slowclk_cycles << 10) + }); + SOC_CLK_RC_SLOW_FREQ_APPROX + } + }; + + let us_time_estimate = (HertzU32::MHz(slowclk_cycles) / expected_freq).to_Hz(); + + // Start calibration + timg0 + .rtccalicfg + .modify(|_, w| w.rtc_cali_start().clear_bit()); + timg0.rtccalicfg.modify(|_, w| w.rtc_cali_start().set_bit()); + + // Wait for calibration to finish up to another us_time_estimate + unsafe { + ets_delay_us(us_time_estimate); + } + + let cal_val = loop { + if timg0.rtccalicfg.read().rtc_cali_rdy().bit_is_set() { + break timg0.rtccalicfg1.read().rtc_cali_value().bits(); + } + + if timg0.rtccalicfg2.read().rtc_cali_timeout().bit_is_set() { + // Timed out waiting for calibration + break 0; + } + }; + + timg0 + .rtccalicfg + .modify(|_, w| w.rtc_cali_start().clear_bit()); + + if cal_clk == RtcCalSel::RtcCal32kXtal && !dig_32k_xtal_enabled { + lp_clkrst + .clk_to_hp + .modify(|_, w| w.icg_hp_xtal32k().clear_bit()); + } + + if cal_clk == RtcCalSel::RtcCalRcFast { + if rc_fast_enabled { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit()); + unsafe { + ets_delay_us(50); + } + } + + if dig_rc_fast_enabled { + lp_clkrst.clk_to_hp.modify(|_, w| w.icg_hp_fosc().set_bit()); + unsafe { + ets_delay_us(5); + } + } + } + + if cal_clk == RtcCalSel::RtcCal32kRc { + if rc32k_enabled { + pmu.hp_sleep_lp_ck_power + .modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit()); + unsafe { + ets_delay_us(300); + } + } + if dig_rc32k_enabled { + lp_clkrst + .clk_to_hp + .modify(|_, w| w.icg_hp_osc32k().set_bit()); + } + } + + cal_val + } + + /// Measure ratio between XTAL frequency and RTC slow clock frequency + fn get_calibration_value(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { + let xtal_freq = RtcClock::get_xtal_freq(); + let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64; + let divider = xtal_freq.mhz() as u64 * slowclk_cycles as u64; + let period_64 = ((xtal_cycles << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider; + + (period_64 & u32::MAX as u64) as u32 + } + + /// Measure RTC slow clock's period, based on main XTAL frequency + /// + /// This function will time out and return 0 if the time for the given + /// number of cycles to be counted exceeds the expected time twice. This + /// may happen if 32k XTAL is being calibrated, but the oscillator has + /// not started up (due to incorrect loading capacitance, board design + /// issue, or lack of 32 XTAL on board). + fn calibrate(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { + let xtal_freq = RtcClock::get_xtal_freq(); + let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64; + let divider = xtal_freq.mhz() as u64 * slowclk_cycles as u64; + let period_64 = ((xtal_cycles << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider; + + (period_64 & u32::MAX as u64) as u32 + } + + /// Calculate the necessary RTC_SLOW_CLK cycles to complete 1 millisecond. + pub(crate) fn cycles_to_1ms() -> u16 { + let period_13q19 = RtcClock::calibrate( + match RtcClock::get_slow_freq() { + RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRtcMux, + RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, + RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, + RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCal32kOscSlow, + // RtcSlowClock::RtcCalRcFast => RtcCalSel::RtcCalRcFast, + }, + 1024, + ); + + // 100_000_000 is used to get rid of `float` calculations + let period = (100_000_000 * period_13q19 as u64) / (1 << RtcClock::CAL_FRACT); + + (100_000_000 * 1000 / period) as u16 + } +} diff --git a/esp-hal-common/src/sha.rs b/esp-hal-common/src/sha.rs index 46084635e20..e7b28c482eb 100644 --- a/esp-hal-common/src/sha.rs +++ b/esp-hal-common/src/sha.rs @@ -296,7 +296,7 @@ impl<'d> Sha<'d> { ShaMode::SHA1 | ShaMode::SHA256 => 64, #[cfg(not(esp32))] ShaMode::SHA224 => 64, - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] _ => 128, }; } diff --git a/esp-hal-common/src/spi.rs b/esp-hal-common/src/spi.rs index 07f6e1a6d29..63c9339fe4a 100644 --- a/esp-hal-common/src/spi.rs +++ b/esp-hal-common/src/spi.rs @@ -1212,7 +1212,7 @@ where } } - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] fn enable_dma(&self) { let reg_block = self.register_block(); reg_block.dma_conf.modify(|_, w| w.dma_tx_ena().set_bit()); @@ -1224,7 +1224,7 @@ where // for non GDMA this is done in `assign_tx_device` / `assign_rx_device` } - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] fn clear_dma_interrupts(&self) { let reg_block = self.register_block(); reg_block.dma_int_clr.write(|w| { @@ -1330,6 +1330,14 @@ pub trait Instance { .set_bit() }); + #[cfg(esp32c6)] + unsafe { + let pcr = &*esp32c6::PCR::PTR; + + // use default clock source PLL_F80M_CLK + pcr.spi2_clkm_conf.modify(|_, w| w.spi2_clkm_sel().bits(1)); + } + reg_block.ctrl.write(|w| unsafe { w.bits(0) }); #[cfg(not(esp32))] @@ -1680,12 +1688,12 @@ pub trait Instance { fn configure_datalen(&self, len: u32) { let reg_block = self.register_block(); - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] reg_block .ms_dlen .write(|w| unsafe { w.ms_data_bitlen().bits(len - 1) }); - #[cfg(not(any(esp32c2, esp32c3, esp32s3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s3)))] { reg_block .mosi_dlen @@ -1698,7 +1706,7 @@ pub trait Instance { } } -#[cfg(any(esp32c2, esp32c3))] +#[cfg(any(esp32c2, esp32c3, esp32c6))] impl Instance for crate::peripherals::SPI2 { #[inline(always)] fn register_block(&self) -> &RegisterBlock { diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index d24cee19829..5a1892040c4 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -10,10 +10,13 @@ //! ``` use crate::peripheral::PeripheralRef; -#[cfg(not(esp32))] -type SystemPeripheral = crate::peripherals::SYSTEM; + #[cfg(esp32)] type SystemPeripheral = crate::peripherals::DPORT; +#[cfg(esp32c6)] +type SystemPeripheral = crate::peripherals::PCR; +#[cfg(not(any(esp32, esp32c6)))] +type SystemPeripheral = crate::peripherals::SYSTEM; /// Peripherals which can be enabled via [PeripheralClockControl] pub enum Peripheral { @@ -26,13 +29,13 @@ pub enum Peripheral { #[cfg(rmt)] Rmt, Ledc, - #[cfg(any(esp32, esp32s3))] + #[cfg(mcpwm)] Mcpwm0, - #[cfg(any(esp32, esp32s3))] + #[cfg(mcpwm)] Mcpwm1, - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(any(esp32, esp32s2, esp32s3, esp32c6))] Pcnt, - #[cfg(any(esp32c2, esp32c3))] + #[cfg(any(esp32c2, esp32c3, esp32c6))] ApbSarAdc, #[cfg(gdma)] Gdma, @@ -40,7 +43,7 @@ pub enum Peripheral { Dma, #[cfg(not(esp32c2))] I2s0, - #[cfg(not(any(esp32c2, esp32s2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s2)))] I2s1, #[cfg(usb_otg)] Usb, @@ -48,6 +51,10 @@ pub enum Peripheral { Twai, #[cfg(aes)] Aes, + #[cfg(esp32c6)] + Twai0, + #[cfg(esp32c6)] + Twai1, } /// Controls the enablement of peripheral clocks. @@ -55,6 +62,7 @@ pub struct PeripheralClockControl { _private: (), } +#[cfg(not(esp32c6))] impl PeripheralClockControl { /// Enables and resets the given peripheral pub fn enable(&mut self, peripheral: Peripheral) { @@ -109,12 +117,12 @@ impl PeripheralClockControl { perip_clk_en0.modify(|_, w| w.ledc_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.ledc_rst().clear_bit()); } - #[cfg(any(esp32, esp32s3))] + #[cfg(mcpwm)] Peripheral::Mcpwm0 => { perip_clk_en0.modify(|_, w| w.pwm0_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.pwm0_rst().clear_bit()); } - #[cfg(any(esp32, esp32s3))] + #[cfg(mcpwm)] Peripheral::Mcpwm1 => { perip_clk_en0.modify(|_, w| w.pwm1_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.pwm1_rst().clear_bit()); @@ -186,6 +194,73 @@ impl PeripheralClockControl { } } +#[cfg(esp32c6)] +impl PeripheralClockControl { + /// Enables and resets the given peripheral + pub fn enable(&mut self, peripheral: Peripheral) { + let system = unsafe { &*SystemPeripheral::PTR }; + + match peripheral { + Peripheral::Spi2 => { + system.spi2_conf.modify(|_, w| w.spi2_clk_en().set_bit()); + system.spi2_conf.modify(|_, w| w.spi2_rst_en().clear_bit()); + } + Peripheral::I2cExt0 => { + system.i2c_conf.modify(|_, w| w.i2c_clk_en().set_bit()); + system.i2c_conf.modify(|_, w| w.i2c_rst_en().clear_bit()); + } + Peripheral::Rmt => { + system.rmt_conf.modify(|_, w| w.rmt_clk_en().set_bit()); + system.rmt_conf.modify(|_, w| w.rmt_rst_en().clear_bit()); + } + Peripheral::Ledc => { + system.ledc_conf.modify(|_, w| w.ledc_clk_en().set_bit()); + system.ledc_conf.modify(|_, w| w.ledc_rst_en().clear_bit()); + } + Peripheral::Mcpwm0 | Peripheral::Mcpwm1 => { + system.pwm_conf.modify(|_, w| w.pwm_clk_en().set_bit()); + system.pwm_conf.modify(|_, w| w.pwm_rst_en().clear_bit()); + } + Peripheral::ApbSarAdc => { + system + .saradc_conf + .modify(|_, w| w.saradc_reg_clk_en().set_bit()); + system + .saradc_conf + .modify(|_, w| w.saradc_reg_rst_en().clear_bit()); + } + Peripheral::Gdma => { + system.gdma_conf.modify(|_, w| w.gdma_clk_en().set_bit()); + system.gdma_conf.modify(|_, w| w.gdma_rst_en().clear_bit()); + } + Peripheral::I2s0 => { + system.i2s_conf.modify(|_, w| w.i2s_clk_en().set_bit()); + system.i2s_conf.modify(|_, w| w.i2s_rst_en().clear_bit()); + } + Peripheral::Twai0 => { + system.twai0_conf.modify(|_, w| w.twai0_clk_en().set_bit()); + system + .twai0_conf + .modify(|_, w| w.twai0_rst_en().clear_bit()); + } + Peripheral::Twai1 => { + system.twai1_conf.modify(|_, w| w.twai1_clk_en().set_bit()); + system + .twai1_conf + .modify(|_, w| w.twai1_rst_en().clear_bit()); + } + Peripheral::Aes => { + system.aes_conf.modify(|_, w| w.aes_clk_en().set_bit()); + system.aes_conf.modify(|_, w| w.aes_rst_en().clear_bit()); + } + Peripheral::Pcnt => { + system.pcnt_conf.modify(|_, w| w.pcnt_clk_en().set_bit()); + system.pcnt_conf.modify(|_, w| w.pcnt_rst_en().clear_bit()); + } + } + } +} + /// Controls the configuration of the chip's clocks. pub struct SystemClockControl { _private: (), @@ -238,13 +313,16 @@ impl<'d, T: crate::peripheral::Peripheral

+ 'd> SystemExt< impl crate::peripheral::Peripheral for SystemClockControl { type P = SystemClockControl; + #[inline] unsafe fn clone_unchecked(&mut self) -> Self::P { SystemClockControl { _private: () } } } + impl crate::peripheral::Peripheral for &mut SystemClockControl { type P = SystemClockControl; + #[inline] unsafe fn clone_unchecked(&mut self) -> Self::P { SystemClockControl { _private: () } diff --git a/esp-hal-common/src/systimer.rs b/esp-hal-common/src/systimer.rs index 09c23bf557f..5bab37803ca 100644 --- a/esp-hal-common/src/systimer.rs +++ b/esp-hal-common/src/systimer.rs @@ -27,12 +27,12 @@ pub struct SystemTimer<'d> { impl<'d> SystemTimer<'d> { #[cfg(esp32s2)] pub const BIT_MASK: u64 = u64::MAX; - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(not(esp32s2))] pub const BIT_MASK: u64 = 0xFFFFFFFFFFFFF; #[cfg(esp32s2)] pub const TICKS_PER_SECOND: u64 = 80_000_000; // TODO this can change when we have support for changing APB frequency - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(not(esp32s2))] pub const TICKS_PER_SECOND: u64 = 16_000_000; pub fn new(p: impl Peripheral

+ 'd) -> Self { @@ -138,7 +138,7 @@ impl Alarm { #[cfg(esp32s2)] systimer.step.write(|w| w.timer_xtal_step().bits(0x1)); // run at XTAL freq, not 80 * XTAL freq - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] { tconf.write(|w| w.target0_timer_unit_sel().clear_bit()); // default, use unit 0 systimer @@ -148,7 +148,7 @@ impl Alarm { conf(tconf, hi, lo); - #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] { match CHANNEL { 0 => { diff --git a/esp-hal-common/src/timer.rs b/esp-hal-common/src/timer.rs index a55eeca0d4a..b43777fb52b 100644 --- a/esp-hal-common/src/timer.rs +++ b/esp-hal-common/src/timer.rs @@ -36,7 +36,7 @@ where { _timer_group: PeripheralRef<'d, T>, pub timer0: Timer>, - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] pub timer1: Timer>, pub wdt: Wdt, } @@ -74,7 +74,7 @@ where clocks.apb_clock, ); - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] let timer1 = Timer::new( Timer1 { phantom: PhantomData::default(), @@ -87,7 +87,7 @@ where Self { _timer_group: timer_group, timer0, - #[cfg(not(any(esp32c2, esp32c3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] timer1, wdt, } @@ -109,6 +109,8 @@ where pub fn new(timg: T, apb_clk_freq: HertzU32) -> Self { // TODO: this currently assumes APB_CLK is being used, as we don't yet have a // way to select the XTAL_CLK. + #[cfg(esp32c6)] + Self::enable_clock(); Self { timg, apb_clk_freq } } @@ -116,6 +118,20 @@ where pub fn free(self) -> T { self.timg } + + #[cfg(esp32c6)] + fn enable_clock() { + let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; + pcr.timergroup0_timer_clk_conf + .write(|w| w.tg0_timer_clk_en().set_bit()); + pcr.timergroup0_timer_clk_conf + .write(|w| unsafe { w.tg0_timer_clk_sel().bits(1) }); + + pcr.timergroup1_timer_clk_conf + .write(|w| w.tg1_timer_clk_en().set_bit()); + pcr.timergroup1_timer_clk_conf + .write(|w| unsafe { w.tg1_timer_clk_sel().bits(1) }); + } } impl Deref for Timer @@ -314,13 +330,13 @@ where } } -#[cfg(not(any(esp32c2, esp32c3)))] +#[cfg(not(any(esp32c2, esp32c3, esp32c6)))] pub struct Timer1 { phantom: PhantomData, } /// Timer peripheral instance -#[cfg(not(any(esp32c2, esp32c3)))] +#[cfg(not(any(esp32c2, esp32c3, esp32c6)))] impl Instance for Timer1 where TG: TimerGroupInstance, @@ -550,11 +566,27 @@ where { /// Create a new watchdog timer instance pub fn new() -> Self { + #[cfg(esp32c6)] + Self::enable_clock(); Self { phantom: PhantomData::default(), } } + #[cfg(esp32c6)] + fn enable_clock() { + let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; + pcr.timergroup0_wdt_clk_conf + .write(|w| w.tg0_wdt_clk_en().set_bit()); + pcr.timergroup0_wdt_clk_conf + .write(|w| unsafe { w.tg0_wdt_clk_sel().bits(1) }); + + pcr.timergroup1_timer_clk_conf + .write(|w| w.tg1_timer_clk_en().set_bit()); + pcr.timergroup1_timer_clk_conf + .write(|w| unsafe { w.tg1_timer_clk_sel().bits(1) }); + } + fn set_wdt_enabled(&mut self, enabled: bool) { let reg_block = unsafe { &*TG::register_block() }; @@ -622,7 +654,7 @@ where .bits(0) }); - #[cfg(any(esp32c2, esp32c3))] + #[cfg(any(esp32c2, esp32c3, esp32c6))] reg_block .wdtconfig0 .modify(|_, w| w.wdt_conf_update_en().set_bit()); diff --git a/esp-hal-common/src/twai/mod.rs b/esp-hal-common/src/twai/mod.rs index 4d76006d7b2..14b88d9234b 100644 --- a/esp-hal-common/src/twai/mod.rs +++ b/esp-hal-common/src/twai/mod.rs @@ -11,11 +11,14 @@ use embedded_hal::can::{Can, Error, ErrorKind, ExtendedId, Frame, Id, StandardId use fugit::HertzU32; use self::filter::{Filter, FilterType}; +#[cfg(not(esp32c6))] +use crate::peripherals::twai::RegisterBlock; +#[cfg(esp32c6)] +use crate::peripherals::twai0::RegisterBlock; use crate::{ clock::Clocks, peripheral::{Peripheral, PeripheralRef}, - peripherals::twai::RegisterBlock, - system::PeripheralClockControl, + system::{self, PeripheralClockControl}, types::{InputSignal, OutputSignal}, InputPin, OutputPin, @@ -137,6 +140,7 @@ pub enum BaudRate { B1000K, Custom(TimingConfig), } + impl BaudRate { /// Convert the BaudRate into the timings that the peripheral needs. // These timings are copied from the ESP IDF C driver. @@ -208,12 +212,12 @@ where baud_rate: BaudRate, ) -> Self { // Enable the peripheral clock for the TWAI peripheral. - clock_control.enable(crate::system::Peripheral::Twai); + clock_control.enable(T::SYSTEM_PERIPHERAL); // Set up the GPIO pins. crate::into_ref!(tx_pin, rx_pin); - tx_pin.connect_peripheral_to_output(OutputSignal::TWAI_TX); - rx_pin.connect_input_to_peripheral(InputSignal::TWAI_RX); + tx_pin.connect_peripheral_to_output(T::OUTPUT_SIGNAL); + rx_pin.connect_input_to_peripheral(T::INPUT_SIGNAL); crate::into_ref!(peripheral); let mut cfg = TwaiConfiguration { peripheral }; @@ -348,6 +352,7 @@ where peripheral: self.peripheral, } } + pub fn receive_error_count(&self) -> u8 { self.peripheral .register_block() @@ -356,6 +361,7 @@ where .rx_err_cnt() .bits() } + pub fn transmit_error_count(&self) -> u8 { self.peripheral .register_block() @@ -388,6 +394,7 @@ where .rx_message_counter() .bits() } + /// Clear the receive FIFO, discarding any valid, partial, or invalid /// packets. /// @@ -417,6 +424,7 @@ pub enum EspTwaiError { BusOff, EmbeddedHAL(ErrorKind), } + impl Error for EspTwaiError { fn kind(&self) -> ErrorKind { match self { @@ -569,6 +577,7 @@ where // embedded-can/embedded-hal trait. nb::Result::Ok(None) } + /// Return a received frame if there are any available. fn receive(&mut self) -> nb::Result { let status = self.peripheral.register_block().status.read(); @@ -702,11 +711,47 @@ where } pub trait Instance { + const SYSTEM_PERIPHERAL: system::Peripheral; + + const INPUT_SIGNAL: InputSignal; + const OUTPUT_SIGNAL: OutputSignal; + fn register_block(&self) -> &RegisterBlock; } -#[cfg(any(esp32s3, esp32c3))] +#[cfg(any(esp32c3, esp32s3))] impl Instance for crate::peripherals::TWAI { + const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai; + + const INPUT_SIGNAL: InputSignal = InputSignal::TWAI_RX; + const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI_TX; + + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } +} + +#[cfg(esp32c6)] +impl Instance for crate::peripherals::TWAI0 { + const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai0; + + const INPUT_SIGNAL: InputSignal = InputSignal::TWAI0_RX; + const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI0_TX; + + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } +} + +#[cfg(esp32c6)] +impl Instance for crate::peripherals::TWAI1 { + const SYSTEM_PERIPHERAL: system::Peripheral = system::Peripheral::Twai1; + + const INPUT_SIGNAL: InputSignal = InputSignal::TWAI1_RX; + const OUTPUT_SIGNAL: OutputSignal = OutputSignal::TWAI1_TX; + #[inline(always)] fn register_block(&self) -> &RegisterBlock { self diff --git a/esp-hal-common/src/uart.rs b/esp-hal-common/src/uart.rs index bc10e55a299..97133b319da 100644 --- a/esp-hal-common/src/uart.rs +++ b/esp-hal-common/src/uart.rs @@ -343,11 +343,13 @@ where .register_block() .clk_conf .modify(|_, w| w.sclk_en().set_bit()); + + self.sync_regs(); } /// Configures the RX-FIFO threshold pub fn set_rx_fifo_full_threshold(&mut self, threshold: u16) { - #[cfg(esp32)] + #[cfg(any(esp32, esp32c6))] let threshold: u8 = threshold as u8; self.uart @@ -592,6 +594,65 @@ where .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); } + #[cfg(esp32c6)] + fn change_baud(&self, baudrate: u32, clocks: &Clocks) { + // we force the clock source to be APB and don't use the decimal part of the + // divider + let clk = clocks.apb_clock.to_Hz(); + let max_div = 0b1111_1111_1111 - 1; + let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); + + // UART clocks are configured via PCR + let pcr = unsafe { &*esp32c6::PCR::PTR }; + + match self.uart.uart_number() { + 0 => { + pcr.uart0_conf + .modify(|_, w| w.uart0_rst_en().clear_bit().uart0_clk_en().set_bit()); + + pcr.uart0_sclk_conf.modify(|_, w| unsafe { + w.uart0_sclk_div_a() + .bits(0) + .uart0_sclk_div_b() + .bits(0) + .uart0_sclk_div_num() + .bits(clk_div as u8 - 1) + .uart0_sclk_sel() + .bits(0x1) // TODO: this probably shouldn't be hard-coded + .uart0_sclk_en() + .set_bit() + }); + } + 1 => { + pcr.uart1_conf + .modify(|_, w| w.uart1_rst_en().clear_bit().uart1_clk_en().set_bit()); + + pcr.uart1_sclk_conf.modify(|_, w| unsafe { + w.uart1_sclk_div_a() + .bits(0) + .uart1_sclk_div_b() + .bits(0) + .uart1_sclk_div_num() + .bits(clk_div as u8 - 1) + .uart1_sclk_sel() + .bits(0x1) // TODO: this probably shouldn't be hard-coded + .uart1_sclk_en() + .set_bit() + }); + } + _ => unreachable!(), // ESP32-C6 only has 2 UART instances + } + + let clk = clk / clk_div; + let divider = clk / baudrate; + let divider = divider as u16; + + self.uart + .register_block() + .clkdiv + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + } + #[cfg(any(esp32, esp32s2))] fn change_baud(&self, baudrate: u32, clocks: &Clocks) { // we force the clock source to be APB and don't use the decimal part of the @@ -609,12 +670,38 @@ where .clkdiv .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); } + + #[cfg(esp32c6)] // TODO introduce a cfg symbol for this + #[inline(always)] + fn sync_regs(&mut self) { + self.uart + .register_block() + .reg_update + .modify(|_, w| w.reg_update().set_bit()); + + while self + .uart + .register_block() + .reg_update + .read() + .reg_update() + .bit_is_set() + { + // wait + } + } + + #[cfg(not(esp32c6))] + #[inline(always)] + fn sync_regs(&mut self) {} } /// UART peripheral instance pub trait Instance { fn register_block(&self) -> &RegisterBlock; + fn uart_number(&self) -> usize; + fn disable_tx_interrupts(&mut self) { self.register_block().int_clr.write(|w| { w.txfifo_empty_int_clr() @@ -710,6 +797,11 @@ impl Instance for UART0 { self } + #[inline(always)] + fn uart_number(&self) -> usize { + 0 + } + fn tx_signal(&self) -> OutputSignal { OutputSignal::U0TXD } @@ -733,6 +825,11 @@ impl Instance for UART1 { self } + #[inline(always)] + fn uart_number(&self) -> usize { + 1 + } + fn tx_signal(&self) -> OutputSignal { OutputSignal::U1TXD } @@ -757,6 +854,11 @@ impl Instance for UART2 { self } + #[inline(always)] + fn uart_number(&self) -> usize { + 2 + } + fn tx_signal(&self) -> OutputSignal { OutputSignal::U2TXD } diff --git a/esp-hal-common/src/utils/smart_leds_adapter.rs b/esp-hal-common/src/utils/smart_leds_adapter.rs index ebed154615a..4c7cd468acb 100644 --- a/esp-hal-common/src/utils/smart_leds_adapter.rs +++ b/esp-hal-common/src/utils/smart_leds_adapter.rs @@ -31,6 +31,8 @@ use crate::{ // #44 have been addressed. #[cfg(esp32c3)] const SOURCE_CLK_FREQ: u32 = 40_000_000; +#[cfg(esp32c6)] +const SOURCE_CLK_FREQ: u32 = 40_000_000; #[cfg(esp32s2)] const SOURCE_CLK_FREQ: u32 = 40_000_000; #[cfg(esp32)] @@ -99,7 +101,7 @@ where where UnconfiguredChannel: OutputChannel = CHANNEL>, { - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32s3))] channel .set_idle_output_level(false) .set_carrier_modulation(false) diff --git a/esp-hal-procmacros/Cargo.toml b/esp-hal-procmacros/Cargo.toml index f130c77312e..dd8ce6ea323 100644 --- a/esp-hal-procmacros/Cargo.toml +++ b/esp-hal-procmacros/Cargo.toml @@ -30,5 +30,6 @@ xtensa = [] esp32 = [] esp32c2 = [] esp32c3 = [] +esp32c6 = [] esp32s2 = [] esp32s3 = [] diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs index 73c385c0970..9d52a1d08c1 100644 --- a/esp-hal-procmacros/src/lib.rs +++ b/esp-hal-procmacros/src/lib.rs @@ -213,6 +213,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let hal_crate = crate_name("esp32c2-hal"); #[cfg(feature = "esp32c3")] let hal_crate = crate_name("esp32c3-hal"); + #[cfg(feature = "esp32c6")] + let hal_crate = crate_name("esp32c6-hal"); #[cfg(feature = "esp32")] let hal_crate_name = Ident::new("esp32_hal", Span::call_site().into()); @@ -224,6 +226,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let hal_crate_name = Ident::new("esp32c2_hal", Span::call_site().into()); #[cfg(feature = "esp32c3")] let hal_crate_name = Ident::new("esp32c3_hal", Span::call_site().into()); + #[cfg(feature = "esp32c6")] + let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into()); let interrupt_in_hal_crate = match hal_crate { Ok(FoundCrate::Itself) => { diff --git a/esp-hal.code-workspace b/esp-hal.code-workspace index 57227a693d1..03134b729f1 100644 --- a/esp-hal.code-workspace +++ b/esp-hal.code-workspace @@ -15,6 +15,9 @@ { "path": "esp32c3-hal" }, + { + "path": "esp32c6-hal" + }, { "path": "esp32s2-hal" }, diff --git a/esp32c6-hal/.cargo/config.toml b/esp32c6-hal/.cargo/config.toml new file mode 100644 index 00000000000..3e8a0bd0188 --- /dev/null +++ b/esp32c6-hal/.cargo/config.toml @@ -0,0 +1,11 @@ +[target.riscv32imac-unknown-none-elf] +runner = "espflash flash --monitor" +rustflags = [ + "-C", "link-arg=-Tlinkall.x", +] + +[build] +target = "riscv32imac-unknown-none-elf" + +[unstable] +build-std = [ "core" ] diff --git a/esp32c6-hal/Cargo.toml b/esp32c6-hal/Cargo.toml new file mode 100644 index 00000000000..cff43a9a025 --- /dev/null +++ b/esp32c6-hal/Cargo.toml @@ -0,0 +1,86 @@ +[package] +name = "esp32c6-hal" +version = "0.1.0" +authors = [ + "Jesse Braham ", + "Björn Quentin ", + "Juraj Sadel ", +] +edition = "2021" +rust-version = "1.60.0" +description = "HAL for ESP32-C6 microcontrollers" +repository = "https://github.com/esp-rs/esp-hal" +license = "MIT OR Apache-2.0" + +keywords = [ + "embedded", + "embedded-hal", + "esp", + "esp32c6", + "no-std", +] +categories = [ + "embedded", + "hardware-support", + "no-std", +] + +[dependencies] +cfg-if = "1.0.0" +embassy-time = { version = "0.1.0", features = ["nightly"], optional = true } +embedded-hal = { version = "0.2.7", features = ["unproven"] } +embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" } +embedded-hal-async = { version = "0.2.0-alpha.0", optional = true } +embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true } +embedded-can = { version = "0.4.1", optional = true } +esp-hal-common = { version = "0.7.0", features = ["esp32c6"], path = "../esp-hal-common" } +r0 = "1.0.0" + +[dev-dependencies] +aes = "0.8.2" +critical-section = "1.1.1" +embassy-executor = { package = "embassy-executor", git = "https://github.com/embassy-rs/embassy/", rev = "cd9a65b", features = ["nightly", "integrated-timers"] } +embedded-graphics = "0.7.1" +esp-backtrace = { git = "https://github.com/jessebraham/esp-backtrace", branch = "feature/chip-support", features = ["esp32c6", "panic-handler", "exception-handler", "print-uart"] } +esp-println = { version = "0.4.0", features = ["esp32c6"] } +sha2 = { version = "0.10.6", default-features = false} +smart-leds = "0.3.0" +ssd1306 = "0.7.1" +static_cell = "1.0.0" + +[features] +default = ["rt", "vectored"] +direct-boot = [] +eh1 = ["esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb", "dep:embedded-can"] +rt = [] +smartled = ["esp-hal-common/smartled"] +ufmt = ["esp-hal-common/ufmt"] +vectored = ["esp-hal-common/vectored"] +async = ["esp-hal-common/async", "embedded-hal-async"] +embassy = ["esp-hal-common/embassy"] +embassy-time-systick = ["esp-hal-common/embassy-time-systick", "embassy-time/tick-hz-16_000_000"] +embassy-time-timg0 = ["esp-hal-common/embassy-time-timg0", "embassy-time/tick-hz-1_000_000"] + +[[example]] +name = "hello_rgb" +required-features = ["smartled"] + +[[example]] +name = "spi_eh1_loopback" +required-features = ["eh1"] + +[[example]] +name = "spi_eh1_device_loopback" +required-features = ["eh1"] + +[[example]] +name = "embassy_hello_world" +required-features = ["embassy"] + +[[example]] +name = "embassy_wait" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_spi" +required-features = ["embassy", "async"] diff --git a/esp32c6-hal/README.md b/esp32c6-hal/README.md new file mode 100644 index 00000000000..1ee4625fa66 --- /dev/null +++ b/esp32c6-hal/README.md @@ -0,0 +1,76 @@ +# esp32c6-hal + +[![Crates.io](https://img.shields.io/crates/v/esp32c6-hal?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp32c6-hal) +[![docs.rs](https://img.shields.io/docsrs/esp32c6-hal?labelColor=1C2C2E&color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp32c6-hal) +![Crates.io](https://img.shields.io/crates/l/esp32c6-hal?labelColor=1C2C2E&style=flat-square) +[![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&labelColor=1C2C2E&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org) + +`no_std` HAL for the ESP32-C6 from Espressif. Implements a number of the traits defined by [embedded-hal](https://github.com/rust-embedded/embedded-hal). + +This device uses the RISC-V ISA, which is officially supported by the Rust compiler via the `riscv32imac-unknown-none-elf` target. Refer to the [Getting Stared](#getting-started) section below for more information. + +## [Documentation] + +[documentation]: https://docs.rs/esp32c6-hal/ + +## Getting Started + +### Installing the Rust Compiler Target + +The compilation target for this device is officially supported via the `stable` release channel and can be installed via [rustup](https://rustup.rs/): + +```shell +$ rustup target add riscv32imac-unknown-none-elf +``` + +### Supported boot methods + +#### IDF Bootloader + +The [IDF second stage bootloader](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-guides/startup.html#second-stage-bootloader) is the default bootloader solution. + +By default, [espflash](https://github.com/esp-rs/espflash) fetches the required binaries (Bootloader and Partition Table) and flashes them onto the target device together with the Rust-based application firmware image. + +#### Direct Boot + +[Direct Boot](https://github.com/espressif/esp32c6-direct-boot-example#direct-boot-in-esp32-c6) allows an application stored in the External Flash to be executed directly, without being copied into Internal RAM. + +##### Booting the Hello World example using Direct Boot + +Build the Hello World example with support for Direct Boot: + +```shell +cargo build --release --example hello_world --features direct-boot +``` + +Then proceed to generating the application binary and flashing it onto the target device: + +```shell +cargo espflash --release --format direct-boot --features direct-boot --example hello_world --monitor +``` + +The ROM Bootloader will identify the firmware image built with Direct Boot support and load it appropriately from the External Flash: + +```shell +ESP-ROM:esp32c6-20220919 +Build:Sep 19 2022 +rst:0x1 (POWERON),boot:0x6e (SPI_FAST_FLASH_BOOT) +Hello world! +Hello world! +Hello world! +``` + +## License + +Licensed under either of: + +- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in +the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without +any additional terms or conditions. diff --git a/esp32c6-hal/build.rs b/esp32c6-hal/build.rs new file mode 100644 index 00000000000..11f038a0be0 --- /dev/null +++ b/esp32c6-hal/build.rs @@ -0,0 +1,79 @@ +use std::{env, fs::File, io::Write, path::PathBuf}; + +#[cfg(feature = "direct-boot")] +fn main() { + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("ld/db-esp32c6-memory.x")) + .unwrap(); + + File::create(out.join("esp32c6-link.x")) + .unwrap() + .write_all(include_bytes!("ld/db-esp32c6-link.x")) + .unwrap(); + + File::create(out.join("riscv-link.x")) + .unwrap() + .write_all(include_bytes!("ld/db-riscv-link.x")) + .unwrap(); + + File::create(out.join("linkall.x")) + .unwrap() + .write_all(include_bytes!("ld/db-linkall.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + + // Only re-run the build script when memory.x is changed, + // instead of when any part of the source code changes. + println!("cargo:rerun-if-changed=ld/memory.x"); + + add_defaults(); +} + +#[cfg(not(feature = "direct-boot"))] +fn main() { + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("ld/bl-esp32c6-memory.x")) + .unwrap(); + + File::create(out.join("bl-riscv-link.x")) + .unwrap() + .write_all(include_bytes!("ld/bl-riscv-link.x")) + .unwrap(); + + File::create(out.join("linkall.x")) + .unwrap() + .write_all(include_bytes!("ld/bl-linkall.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + + // Only re-run the build script when memory.x is changed, + // instead of when any part of the source code changes. + println!("cargo:rerun-if-changed=ld/memory.x"); + + add_defaults(); +} + +fn add_defaults() { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + File::create(out.join("hal-defaults.x")) + .unwrap() + .write_all(include_bytes!("ld/hal-defaults.x")) + .unwrap(); + + File::create(out.join("rom-functions.x")) + .unwrap() + .write_all(include_bytes!("ld/rom-functions.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); +} diff --git a/esp32c6-hal/examples/adc.rs b/esp32c6-hal/examples/adc.rs new file mode 100644 index 00000000000..592aa0d4833 --- /dev/null +++ b/esp32c6-hal/examples/adc.rs @@ -0,0 +1,63 @@ +//! Connect a potentiometer to PIN2 and see the read values change when +//! rotating the shaft. Alternatively you could also connect the PIN to GND or +//! 3V3 to see the maximum and minimum raw values read. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + adc::{AdcConfig, Attenuation, ADC, ADC1}, + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Create ADC instances + let analog = peripherals.APB_SARADC.split(); + + let mut adc1_config = AdcConfig::new(); + + let mut pin = adc1_config.enable_pin(io.pins.gpio2.into_analog(), Attenuation::Attenuation11dB); + + let mut adc1 = ADC::::adc( + &mut system.peripheral_clock_control, + analog.adc1, + adc1_config, + ) + .unwrap(); + + let mut delay = Delay::new(&clocks); + + loop { + let pin_value: u16 = nb::block!(adc1.read(&mut pin)).unwrap(); + println!("PIN2 ADC reading = {}", pin_value); + delay.delay_ms(1500u32); + } +} diff --git a/esp32c6-hal/examples/advanced_serial.rs b/esp32c6-hal/examples/advanced_serial.rs new file mode 100644 index 00000000000..b76ea57d8ad --- /dev/null +++ b/esp32c6-hal/examples/advanced_serial.rs @@ -0,0 +1,75 @@ +//! This shows how to configure UART +//! You can short the TX and RX pin and see it reads what was written. +//! Additionally you can connect a logic analzyer to TX and see how the changes +//! of the configuration change the output signal. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + uart::{ + config::{Config, DataBits, Parity, StopBits}, + TxRxPins, + }, + Rtc, + Uart, + IO, +}; +use esp_backtrace as _; +use esp_println::println; +use nb::block; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let config = Config { + baudrate: 115200, + data_bits: DataBits::DataBits8, + parity: Parity::ParityNone, + stop_bits: StopBits::STOP1, + }; + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let pins = TxRxPins::new_tx_rx( + io.pins.gpio1.into_push_pull_output(), + io.pins.gpio2.into_floating_input(), + ); + + let mut serial1 = Uart::new_with_config(peripherals.UART1, Some(config), Some(pins), &clocks); + + timer0.start(250u64.millis()); + + println!("Start"); + loop { + serial1.write(0x42).ok(); + let read = block!(serial1.read()); + + match read { + Ok(read) => println!("Read {:02x}", read), + Err(err) => println!("Error {:?}", err), + } + + block!(timer0.wait()).unwrap(); + } +} diff --git a/esp32c6-hal/examples/aes.rs b/esp32c6-hal/examples/aes.rs new file mode 100644 index 00000000000..e0c6861a2b3 --- /dev/null +++ b/esp32c6-hal/examples/aes.rs @@ -0,0 +1,102 @@ +#![no_std] +#![no_main] +use aes::{ + cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, KeyInit}, + Aes128 as Aes128SW, +}; +use esp32c6_hal::{ + aes::{Aes, Aes128, Cipher, Key}, + clock::ClockControl, + peripherals::Peripherals, + prelude::*, + systimer::SystemTimer, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let mut aes = Aes::new(peripherals.AES, &mut system.peripheral_clock_control); + + let keytext = "SUp4SeCp@sSw0rd".as_bytes(); + let plaintext = "message".as_bytes(); + + // create an array with aes128 key size + let mut keybuf = [0_u8; 16]; + keybuf[..keytext.len()].copy_from_slice(keytext); + + // create an array with aes block size + let mut block_buf = [0_u8; 16]; + block_buf[..plaintext.len()].copy_from_slice(plaintext); + + let key = Key::::from(&keybuf); + let mut cipher = Cipher::new(&mut aes, &key); + let mut block = block_buf.clone(); + let pre_hw_encrypt = SystemTimer::now(); + cipher.encrypt_block(&mut block); + let post_hw_encrypt = SystemTimer::now(); + println!( + "it took {} cycles for hw encrypt", + post_hw_encrypt - pre_hw_encrypt + ); + let hw_encrypted = block.clone(); + let pre_hw_decrypt = SystemTimer::now(); + cipher.decrypt_block(&mut block); + let post_hw_decrypt = SystemTimer::now(); + println!( + "it took {} cycles for hw decrypt", + post_hw_decrypt - pre_hw_decrypt + ); + let hw_decrypted = block; + + let key = GenericArray::from(keybuf); + let mut block = GenericArray::from(block_buf); + let cipher = Aes128SW::new(&key); + let pre_sw_encrypt = SystemTimer::now(); + cipher.encrypt_block(&mut block); + let post_sw_encrypt = SystemTimer::now(); + println!( + "it took {} cycles for sw encrypt", + post_sw_encrypt - pre_sw_encrypt + ); + let sw_encrypted = block.clone(); + let pre_sw_decrypt = SystemTimer::now(); + cipher.decrypt_block(&mut block); + let post_sw_decrypt = SystemTimer::now(); + println!( + "it took {} cycles for sw decrypt", + post_sw_decrypt - pre_sw_decrypt + ); + let sw_decrypted = block; + + assert!(eq(&sw_encrypted.into(), &hw_encrypted)); + assert!(eq(&sw_decrypted.into(), &hw_decrypted)); + + println!("done"); + + loop {} +} +fn eq(slice1: &[u8; 16], slice2: &[u8; 16]) -> bool { + slice1.iter().zip(slice2.iter()).all(|(a, b)| a == b) +} diff --git a/esp32c6-hal/examples/blinky.rs b/esp32c6-hal/examples/blinky.rs new file mode 100644 index 00000000000..8f59ed87e47 --- /dev/null +++ b/esp32c6-hal/examples/blinky.rs @@ -0,0 +1,52 @@ +//! Blinks an LED +//! +//! This assumes that a LED is connected to the pin assigned to `led`. (GPIO5) + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + // Set GPIO5 as an output, and set its state high initially. + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = io.pins.gpio5.into_push_pull_output(); + + led.set_high().unwrap(); + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(&clocks); + + loop { + led.toggle().unwrap(); + delay.delay_ms(500u32); + } +} diff --git a/esp32c6-hal/examples/blinky_erased_pins.rs b/esp32c6-hal/examples/blinky_erased_pins.rs new file mode 100644 index 00000000000..776a31e14d8 --- /dev/null +++ b/esp32c6-hal/examples/blinky_erased_pins.rs @@ -0,0 +1,67 @@ +//! Blinks an LED +//! +//! This assumes that LEDs are connected to GPIO3, 4 and 5. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + gpio::{AnyPin, Input, Output, PullDown, PushPull, IO}, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + // Set GPIO4 as an output, and set its state high initially. + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let led1 = io.pins.gpio3.into_push_pull_output(); + let led2 = io.pins.gpio4.into_push_pull_output(); + let led3 = io.pins.gpio5.into_push_pull_output(); + + let button = io.pins.gpio9.into_pull_down_input().degrade(); + + // you can use `into` or `degrade` + let mut pins = [led1.into(), led2.into(), led3.degrade()]; + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(&clocks); + + loop { + toggle_pins(&mut pins, &button); + delay.delay_ms(500u32); + } +} + +fn toggle_pins(leds: &mut [AnyPin>], button: &AnyPin>) { + for pin in leds.iter_mut() { + pin.toggle().unwrap(); + } + + if button.is_low().unwrap() { + esp_println::println!("Button"); + } +} diff --git a/esp32c6-hal/examples/embassy_hello_world.rs b/esp32c6-hal/examples/embassy_hello_world.rs new file mode 100644 index 00000000000..197d4b56411 --- /dev/null +++ b/esp32c6-hal/examples/embassy_hello_world.rs @@ -0,0 +1,74 @@ +//! embassy hello world +//! +//! This is an example of running the embassy executor with multiple tasks +//! concurrently. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32c6_hal::{ + clock::ClockControl, + embassy, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use static_cell::StaticCell; + +#[embassy_executor::task] +async fn run1() { + loop { + esp_println::println!("Hello world from embassy using esp-hal-async!"); + Timer::after(Duration::from_millis(1_000)).await; + } +} + +#[embassy_executor::task] +async fn run2() { + loop { + esp_println::println!("Bing!"); + Timer::after(Duration::from_millis(5_000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(run1()).ok(); + spawner.spawn(run2()).ok(); + }); +} diff --git a/esp32c6-hal/examples/embassy_spi.rs b/esp32c6-hal/examples/embassy_spi.rs new file mode 100644 index 00000000000..45918941aa3 --- /dev/null +++ b/esp32c6-hal/examples/embassy_spi.rs @@ -0,0 +1,135 @@ +//! embassy hello world +//! +//! This is an example of running the embassy executor with multiple tasks +//! concurrently. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32c6_hal::{ + clock::ClockControl, + dma::{DmaPriority, *}, + embassy, + gdma::*, + peripherals::Peripherals, + prelude::*, + spi::{dma::SpiDma, Spi, SpiMode}, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use static_cell::StaticCell; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +pub type SpiType<'d> = SpiDma< + 'd, + esp32c6_hal::peripherals::SPI2, + ChannelTx<'d, Channel0TxImpl, esp32c6_hal::gdma::Channel0>, + ChannelRx<'d, Channel0RxImpl, esp32c6_hal::gdma::Channel0>, + SuitablePeripheral0, +>; + +#[embassy_executor::task] +async fn spi_task(spi: &'static mut SpiType<'static>) { + let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; + loop { + let mut buffer = [0; 8]; + esp_println::println!("Sending bytes"); + embedded_hal_async::spi::SpiBus::transfer(spi, &mut buffer, &send_buffer) + .await + .unwrap(); + esp_println::println!("Bytes recieved: {:?}", buffer); + Timer::after(Duration::from_millis(5_000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + esp32c6_hal::interrupt::enable( + esp32c6_hal::peripherals::Interrupt::DMA_IN_CH0, + esp32c6_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + esp32c6_hal::interrupt::enable( + esp32c6_hal::peripherals::Interrupt::DMA_OUT_CH0, + esp32c6_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + let cs = io.pins.gpio10; + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let descriptors = singleton!([0u32; 8 * 3]); + let rx_descriptors = singleton!([0u32; 8 * 3]); + + let spi = singleton!(Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + descriptors, + rx_descriptors, + DmaPriority::Priority0, + ))); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(spi_task(spi)).ok(); + }); +} diff --git a/esp32c6-hal/examples/embassy_wait.rs b/esp32c6-hal/examples/embassy_wait.rs new file mode 100644 index 00000000000..90cf3a8c8b6 --- /dev/null +++ b/esp32c6-hal/examples/embassy_wait.rs @@ -0,0 +1,80 @@ +//! embassy wait +//! +//! This is an example of asynchronously `Wait`ing for a pin state to change. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::digital::Wait; +use esp32c6_hal::{ + clock::ClockControl, + embassy, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_hal_common::{Gpio9, Input, PullDown}; +use static_cell::StaticCell; + +#[embassy_executor::task] +async fn ping(mut pin: Gpio9>) { + loop { + esp_println::println!("Waiting..."); + pin.wait_for_rising_edge().await.unwrap(); + esp_println::println!("Ping!"); + Timer::after(Duration::from_millis(100)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + // GPIO 9 as input + let input = io.pins.gpio9.into_pull_down_input(); + + // Async requires the GPIO interrupt to wake futures + esp32c6_hal::interrupt::enable( + esp32c6_hal::peripherals::Interrupt::GPIO, + esp32c6_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(ping(input)).ok(); + }); +} diff --git a/esp32c6-hal/examples/gpio_interrupt.rs b/esp32c6-hal/examples/gpio_interrupt.rs new file mode 100644 index 00000000000..64d6de3b4a1 --- /dev/null +++ b/esp32c6-hal/examples/gpio_interrupt.rs @@ -0,0 +1,81 @@ +//! GPIO interrupt +//! +//! This prints "Interrupt" when the boot button is pressed. +//! It also blinks an LED like the blinky example. + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp32c6_hal::{ + clock::ClockControl, + gpio::{Event, Gpio9, Input, PullDown, IO}, + interrupt, + peripherals::{self, Peripherals}, + prelude::*, + riscv, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; + +static BUTTON: Mutex>>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + // Set GPIO5 as an output + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = io.pins.gpio5.into_push_pull_output(); + + // Set GPIO9 as an input + let mut button = io.pins.gpio9.into_pull_down_input(); + button.listen(Event::FallingEdge); + + critical_section::with(|cs| BUTTON.borrow_ref_mut(cs).replace(button)); + + interrupt::enable(peripherals::Interrupt::GPIO, interrupt::Priority::Priority3).unwrap(); + + unsafe { + riscv::interrupt::enable(); + } + + let mut delay = Delay::new(&clocks); + loop { + led.toggle().unwrap(); + delay.delay_ms(500u32); + } +} + +#[interrupt] +fn GPIO() { + critical_section::with(|cs| { + esp_println::println!("Interrupt"); + + BUTTON + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt(); + }); +} diff --git a/esp32c6-hal/examples/hello_rgb.rs b/esp32c6-hal/examples/hello_rgb.rs new file mode 100644 index 00000000000..7ed5a70d2b2 --- /dev/null +++ b/esp32c6-hal/examples/hello_rgb.rs @@ -0,0 +1,95 @@ +//! //! RGB LED Demo +//! +//! This example drives an SK68XX RGB LED that is connected to the GPIO8 pin. +//! A RGB LED is connected to that pin on the ESP32-C6-DevKitC-1 and board. +//! +//! The demo will leverage the [`smart_leds`](https://crates.io/crates/smart-leds) +//! crate functionality to circle through the HSV hue color space (with +//! saturation and value both at 255). Additionally, we apply a gamma correction +//! and limit the brightness to 10 (out of 255). +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + peripherals, + prelude::*, + pulse_control::ClockSource, + timer::TimerGroup, + utils::{smartLedAdapter, SmartLedsAdapter}, + Delay, + PulseControl, + Rtc, + IO, +}; +use esp_backtrace as _; +use smart_leds::{ + brightness, + gamma, + hsv::{hsv2rgb, Hsv}, + SmartLedsWrite, +}; + +#[entry] +fn main() -> ! { + let peripherals = peripherals::Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + // Configure RMT peripheral globally + let pulse = PulseControl::new( + peripherals.RMT, + &mut system.peripheral_clock_control, + ClockSource::APB, + 0, + 0, + 0, + ) + .unwrap(); + + // We use one of the RMT channels to instantiate a `SmartLedsAdapter` which can + // be used directly with all `smart_led` implementations + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = ::new(pulse.channel0, io.pins.gpio8); + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(&clocks); + + let mut color = Hsv { + hue: 0, + sat: 255, + val: 255, + }; + let mut data; + + loop { + // Iterate over the rainbow! + for hue in 0..=255 { + color.hue = hue; + // Convert from the HSV color space (where we can easily transition from one + // color to the other) to the RGB color space that we can then send to the LED + data = [hsv2rgb(color)]; + // When sending to the LED, we do a gamma correction first (see smart_leds + // documentation for details) and then limit the brightness to 10 out of 255 so + // that the output it's not too bright. + led.write(brightness(gamma(data.iter().cloned()), 10)) + .unwrap(); + delay.delay_ms(20u8); + } + } +} diff --git a/esp32c6-hal/examples/hello_world.rs b/esp32c6-hal/examples/hello_world.rs new file mode 100644 index 00000000000..c7c35d53b49 --- /dev/null +++ b/esp32c6-hal/examples/hello_world.rs @@ -0,0 +1,46 @@ +//! This shows how to write text to uart0. +//! You can see the output with `espflash` if you provide the `--monitor` option + +#![no_std] +#![no_main] + +use core::fmt::Write; + +use esp32c6_hal::{ + clock::ClockControl, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + Uart, +}; +use esp_backtrace as _; +use nb::block; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut uart0 = Uart::new(peripherals.UART0); + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + timer0.start(1u64.secs()); + + loop { + writeln!(uart0, "Hello world!").unwrap(); + block!(timer0.wait()).unwrap(); + } +} diff --git a/esp32c6-hal/examples/i2c_bmp180_calibration_data.rs b/esp32c6-hal/examples/i2c_bmp180_calibration_data.rs new file mode 100644 index 00000000000..3181beee3e2 --- /dev/null +++ b/esp32c6-hal/examples/i2c_bmp180_calibration_data.rs @@ -0,0 +1,61 @@ +//! Read calibration data from BMP180 sensor +//! +//! This example dumps the calibration data from a BMP180 sensor +//! +//! The following wiring is assumed: +//! - SDA => GPIO1 +//! - SCL => GPIO2 + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + i2c::I2C, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Create a new peripheral object with the described wiring + // and standard I2C clock speed + let mut i2c = I2C::new( + peripherals.I2C0, + io.pins.gpio1, + io.pins.gpio2, + 100u32.kHz(), + &mut system.peripheral_clock_control, + &clocks, + ); + + loop { + let mut data = [0u8; 22]; + i2c.write_read(0x77, &[0xaa], &mut data).ok(); + + println!("{:02x?}", data); + } +} diff --git a/esp32c6-hal/examples/i2c_display.rs b/esp32c6-hal/examples/i2c_display.rs new file mode 100644 index 00000000000..399857ae523 --- /dev/null +++ b/esp32c6-hal/examples/i2c_display.rs @@ -0,0 +1,133 @@ +//! I2C Display example +//! +//! This example prints some text on an SSD1306-based +//! display (via I2C) +//! +//! The following wiring is assumed: +//! - SDA => GPIO1 +//! - SCL => GPIO2 + +#![no_std] +#![no_main] + +use embedded_graphics::{ + mono_font::{ + ascii::{FONT_6X10, FONT_9X18_BOLD}, + MonoTextStyleBuilder, + }, + pixelcolor::BinaryColor, + prelude::*, + text::{Alignment, Text}, +}; +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + i2c::I2C, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use nb::block; +use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Create a new peripheral object with the described wiring + // and standard I2C clock speed + let i2c = I2C::new( + peripherals.I2C0, + io.pins.gpio1, + io.pins.gpio2, + 100u32.kHz(), + &mut system.peripheral_clock_control, + &clocks, + ); + + // Start timer (5 second interval) + timer0.start(5u64.secs()); + + // Initialize display + let interface = I2CDisplayInterface::new(i2c); + let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) + .into_buffered_graphics_mode(); + display.init().unwrap(); + + // Specify different text styles + let text_style = MonoTextStyleBuilder::new() + .font(&FONT_6X10) + .text_color(BinaryColor::On) + .build(); + let text_style_big = MonoTextStyleBuilder::new() + .font(&FONT_9X18_BOLD) + .text_color(BinaryColor::On) + .build(); + + loop { + // Fill display bufffer with a centered text with two lines (and two text + // styles) + Text::with_alignment( + "esp-hal", + display.bounding_box().center() + Point::new(0, 0), + text_style_big, + Alignment::Center, + ) + .draw(&mut display) + .unwrap(); + + Text::with_alignment( + "Chip: ESP32-C6", + display.bounding_box().center() + Point::new(0, 14), + text_style, + Alignment::Center, + ) + .draw(&mut display) + .unwrap(); + + // Write buffer to display + display.flush().unwrap(); + // Clear display buffer + display.clear(); + + // Wait 5 seconds + block!(timer0.wait()).unwrap(); + + // Write single-line centered text "Hello World" to buffer + Text::with_alignment( + "Hello World!", + display.bounding_box().center(), + text_style_big, + Alignment::Center, + ) + .draw(&mut display) + .unwrap(); + + // Write buffer to display + display.flush().unwrap(); + // Clear display buffer + display.clear(); + + // Wait 5 seconds + block!(timer0.wait()).unwrap(); + } +} diff --git a/esp32c6-hal/examples/i2s_read.rs b/esp32c6-hal/examples/i2s_read.rs new file mode 100644 index 00000000000..f6ad4289331 --- /dev/null +++ b/esp32c6-hal/examples/i2s_read.rs @@ -0,0 +1,100 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + i2s::{DataFormat, I2s, I2s0New, I2sReadDma, MclkPin, PinsBclkWsDin, Standard}, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut tx_descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio5, + )); + + let buffer = dma_buffer(); + + let mut transfer = i2s_rx.read_dma_circular(buffer).unwrap(); + println!("Started transfer"); + + loop { + let avail = transfer.available(); + + if avail > 0 { + let mut rcv = [0u8; 5000]; + transfer.pop(&mut rcv[..avail]).unwrap(); + println!("Received {:x?}...", &rcv[..30]); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32c6-hal/examples/i2s_sound.rs b/esp32c6-hal/examples/i2s_sound.rs new file mode 100644 index 00000000000..8f79b53a518 --- /dev/null +++ b/esp32c6-hal/examples/i2s_sound.rs @@ -0,0 +1,140 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + i2s::{DataFormat, I2s, I2s0New, I2sWriteDma, MclkPin, PinsBclkWsDout, Standard}, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut tx_descriptors = [0u32; 20 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + + let mut transfer = i2s_tx.write_dma_circular(buffer).unwrap(); + loop { + let avail = transfer.available(); + if avail > 0 { + let avail = usize::min(10000, avail); + for bidx in 0..avail { + filler[bidx] = data[idx]; + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + transfer.push(&filler[0..avail]).unwrap(); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32c6-hal/examples/ledc.rs b/esp32c6-hal/examples/ledc.rs new file mode 100644 index 00000000000..02066ce3f1d --- /dev/null +++ b/esp32c6-hal/examples/ledc.rs @@ -0,0 +1,74 @@ +//! Turns on LED with the option to change LED intensity depending on `duty` +//! value. Possible values (`u32`) are in range 0..100. +//! +//! This assumes that a LED is connected to the pin assigned to `led`. (GPIO4) + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + ledc::{ + channel::{self, ChannelIFace}, + timer::{self, TimerIFace}, + LSGlobalClkSource, + LowSpeed, + LEDC, + }, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let led = io.pins.gpio4.into_push_pull_output(); + + let mut ledc = LEDC::new( + peripherals.LEDC, + &clocks, + &mut system.peripheral_clock_control, + ); + ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); + let mut lstimer0 = ledc.get_timer::(timer::Number::Timer2); + + lstimer0 + .configure(timer::config::Config { + duty: timer::config::Duty::Duty5Bit, + clock_source: timer::LSClockSource::APBClk, + frequency: 24u32.kHz(), + }) + .unwrap(); + + let mut channel0 = ledc.get_channel(channel::Number::Channel0, led); + channel0 + .configure(channel::config::Config { + timer: &lstimer0, + duty_pct: 10, + }) + .unwrap(); + + loop {} +} diff --git a/esp32c6-hal/examples/mcpwm.rs b/esp32c6-hal/examples/mcpwm.rs new file mode 100644 index 00000000000..030fa40cbdd --- /dev/null +++ b/esp32c6-hal/examples/mcpwm.rs @@ -0,0 +1,69 @@ +//! Uses timer0 and operator0 of the MCPWM0 peripheral to output a 50% duty +//! signal at 20 kHz. +//! +//! The signal will be output to the pin assigned to `pin`. (GPIO4) + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + mcpwm::{operator::PwmPinConfig, timer::PwmWorkingMode, PeripheralClockConfig, MCPWM}, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let pin = io.pins.gpio4; + + // initialize peripheral + let clock_cfg = PeripheralClockConfig::with_frequency(&clocks, 40u32.MHz()).unwrap(); + let mut mcpwm = MCPWM::new( + peripherals.MCPWM, + clock_cfg, + &mut system.peripheral_clock_control, + ); + + // connect operator0 to timer0 + mcpwm.operator0.set_timer(&mcpwm.timer0); + // connect operator0 to pin + let mut pwm_pin = mcpwm + .operator0 + .with_pin_a(pin, PwmPinConfig::UP_ACTIVE_HIGH); + + // start timer with timestamp values in the range of 0..=99 and a frequency of + // 20 kHz + let timer_clock_cfg = clock_cfg + .timer_clock_with_frequency(99, PwmWorkingMode::Increase, 20u32.kHz()) + .unwrap(); + mcpwm.timer0.start(timer_clock_cfg); + + // pin will be high 50% of the time + pwm_pin.set_timestamp(50); + + loop {} +} diff --git a/esp32c6-hal/examples/pcnt_encoder.rs b/esp32c6-hal/examples/pcnt_encoder.rs new file mode 100644 index 00000000000..858b58ecbab --- /dev/null +++ b/esp32c6-hal/examples/pcnt_encoder.rs @@ -0,0 +1,149 @@ +//! PCNT Encoder Demo +//! +//! This example decodes a quadrature encoder +//! +//! Since the PCNT units reset to zero when they reach their limits +//! we enable an interrupt on the upper and lower limits and +//! track the overflow in an AtomicI32 + +#![no_std] +#![no_main] +use core::{ + cell::RefCell, + cmp::min, + sync::atomic::{AtomicI32, Ordering}, +}; + +use critical_section::Mutex; +use esp32c6_hal as esp_hal; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + interrupt, + pcnt::{channel, channel::PcntSource, unit, PCNT}, + peripherals::{self, Peripherals}, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_println::println; + +static UNIT0: Mutex>> = Mutex::new(RefCell::new(None)); +static VALUE: AtomicI32 = AtomicI32::new(0); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let unit_number = unit::Number::Unit1; + + // setup a pulse couter + println!("setup pulse counter unit 0"); + let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control); + let mut u0 = pcnt.get_unit(unit_number); + u0.configure(unit::Config { + low_limit: -100, + high_limit: 100, + filter: Some(min(10u16 * 80, 1023u16)), + ..Default::default() + }) + .unwrap(); + + println!("setup channel 0"); + let mut ch0 = u0.get_channel(channel::Number::Channel0); + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut pin_a = io.pins.gpio5.into_pull_up_input(); + let mut pin_b = io.pins.gpio6.into_pull_up_input(); + + ch0.configure( + PcntSource::from_pin(&mut pin_a), + PcntSource::from_pin(&mut pin_b), + channel::Config { + lctrl_mode: channel::CtrlMode::Reverse, + hctrl_mode: channel::CtrlMode::Keep, + pos_edge: channel::EdgeMode::Decrement, + neg_edge: channel::EdgeMode::Increment, + invert_ctrl: false, + invert_sig: false, + }, + ); + + println!("setup channel 1"); + let mut ch1 = u0.get_channel(channel::Number::Channel1); + ch1.configure( + PcntSource::from_pin(&mut pin_b), + PcntSource::from_pin(&mut pin_a), + channel::Config { + lctrl_mode: channel::CtrlMode::Reverse, + hctrl_mode: channel::CtrlMode::Keep, + pos_edge: channel::EdgeMode::Increment, + neg_edge: channel::EdgeMode::Decrement, + invert_ctrl: false, + invert_sig: false, + }, + ); + println!("subscribing to events"); + u0.events(unit::Events { + low_limit: true, + high_limit: true, + thresh0: false, + thresh1: false, + zero: false, + }); + + println!("enabling interrupts"); + u0.listen(); + println!("resume pulse counter unit 0"); + u0.resume(); + + critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0)); + + interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap(); + + let mut last_value: i32 = 0; + loop { + critical_section::with(|cs| { + let mut u0 = UNIT0.borrow_ref_mut(cs); + let u0 = u0.as_mut().unwrap(); + let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst); + if value != last_value { + println!("value: {value}"); + last_value = value; + } + }); + } +} + +#[interrupt] +fn PCNT() { + critical_section::with(|cs| { + let mut u0 = UNIT0.borrow_ref_mut(cs); + let u0 = u0.as_mut().unwrap(); + if u0.interrupt_set() { + let events = u0.get_events(); + if events.high_limit { + VALUE.fetch_add(100, Ordering::SeqCst); + } else if events.low_limit { + VALUE.fetch_add(-100, Ordering::SeqCst); + } + u0.reset_interrupt(); + } + }); +} diff --git a/esp32c6-hal/examples/pulse_control.rs b/esp32c6-hal/examples/pulse_control.rs new file mode 100644 index 00000000000..597735e9ede --- /dev/null +++ b/esp32c6-hal/examples/pulse_control.rs @@ -0,0 +1,90 @@ +//! This demos basic usage of RMT / PulseControl +//! Use a logic analyzer to see the generated pulses. +//! The correct output is only achieved when running in release mode. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + pulse_control::{ClockSource, ConfiguredChannel, OutputChannel, PulseCode, RepeatMode}, + timer::TimerGroup, + PulseControl, + Rtc, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Configure RMT peripheral globally + let pulse = PulseControl::new( + peripherals.RMT, + &mut system.peripheral_clock_control, + ClockSource::APB, + 0, + 0, + 0, + ) + .unwrap(); + + let mut rmt_channel0 = pulse.channel0; + + // Set up channel + rmt_channel0 + .set_idle_output_level(false) + .set_carrier_modulation(false) + .set_channel_divider(1) + .set_idle_output(true); + + // Assign GPIO pin where pulses should be sent to + let mut rmt_channel0 = rmt_channel0.assign_pin(io.pins.gpio4); + + // Create pulse sequence + let mut seq = [PulseCode { + level1: true, + length1: 0u32.nanos(), + level2: false, + length2: 0u32.nanos(), + }; 128]; + + // -1 to make sure that the last element is a transmission end marker (i.e. + // lenght 0) + for i in 0..(seq.len() - 1) { + seq[i] = PulseCode { + level1: true, + length1: (10u32 * (i as u32 + 1u32)).nanos(), + level2: false, + length2: 60u32.nanos(), + }; + } + + loop { + // Send sequence + rmt_channel0 + .send_pulse_sequence(RepeatMode::SingleShot, &seq) + .unwrap(); + } +} diff --git a/esp32c6-hal/examples/ram.rs b/esp32c6-hal/examples/ram.rs new file mode 100644 index 00000000000..1a66ce75318 --- /dev/null +++ b/esp32c6-hal/examples/ram.rs @@ -0,0 +1,99 @@ +//! This shows how to use RTC memory. +//! RTC memory is retained during resets and during most sleep modes. +//! Initialized memory is always re-initialized on startup. +//! Uninitialzed memory isn't initialized on startup and can be used to keep +//! data during resets. Zeroed memory is initialized to zero on startup. +//! We can also run code from RTC memory. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + macros::ram, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, +}; +use esp_backtrace as _; +use esp_println::println; +use nb::block; + +#[ram(rtc_fast)] +static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb]; + +#[ram(rtc_fast, uninitialized)] +static mut SOME_UNINITED_DATA: [u8; 2] = [0; 2]; + +#[ram(rtc_fast, zeroed)] +static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8]; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable MWDT flash boot protection + wdt0.disable(); + wdt1.disable(); + // The RWDT flash boot protection remains enabled and it being triggered is part + // of the example + + timer0.start(1u64.secs()); + + println!("RAM function located at {:p}", function_in_ram as *const ()); + unsafe { + println!("SOME_INITED_DATA {:x?}", SOME_INITED_DATA); + println!("SOME_UNINITED_DATA {:x?}", SOME_UNINITED_DATA); + println!("SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA); + + SOME_INITED_DATA[0] = 0xff; + SOME_ZEROED_DATA[0] = 0xff; + + println!("SOME_INITED_DATA {:x?}", SOME_INITED_DATA); + println!("SOME_UNINITED_DATA {:x?}", SOME_UNINITED_DATA); + println!("SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA); + + if SOME_UNINITED_DATA[0] != 0 { + SOME_UNINITED_DATA[0] = 0; + SOME_UNINITED_DATA[1] = 0; + } + + if SOME_UNINITED_DATA[1] == 0xff { + SOME_UNINITED_DATA[1] = 0; + } + + println!("Counter {}", SOME_UNINITED_DATA[1]); + SOME_UNINITED_DATA[1] += 1; + } + + println!( + "RTC_FAST function located at {:p}", + function_in_rtc_ram as *const () + ); + println!("Result {}", function_in_rtc_ram()); + + loop { + function_in_ram(); + block!(timer0.wait()).unwrap(); + } +} + +#[ram] +fn function_in_ram() { + println!("Hello world!"); +} + +#[ram(rtc_fast)] +fn function_in_rtc_ram() -> u32 { + 42 +} diff --git a/esp32c6-hal/examples/read_efuse.rs b/esp32c6-hal/examples/read_efuse.rs new file mode 100644 index 00000000000..f5534858a35 --- /dev/null +++ b/esp32c6-hal/examples/read_efuse.rs @@ -0,0 +1,40 @@ +//! This shows how to read selected information from eFuses. +//! e.g. the MAC address + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + efuse::Efuse, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + println!("MAC address {:02x?}", Efuse::get_mac_address()); + println!("Flash Encryption {:?}", Efuse::get_flash_encryption()); + + loop {} +} diff --git a/esp32c6-hal/examples/rtc_watchdog.rs b/esp32c6-hal/examples/rtc_watchdog.rs new file mode 100644 index 00000000000..73601b1d70f --- /dev/null +++ b/esp32c6-hal/examples/rtc_watchdog.rs @@ -0,0 +1,78 @@ +//! This demos the RTC Watchdog Timer (RWDT). +//! The RWDT is initially configured to trigger an interrupt after a given +//! timeout. Then, upon expiration, the RWDT is restarted and then reconfigured +//! to reset both the main system and the RTC. + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp32c6_hal::{ + clock::ClockControl, + interrupt, + peripherals::{self, Peripherals}, + prelude::*, + riscv, + timer::TimerGroup, + Rtc, + Rwdt, +}; +use esp_backtrace as _; + +static RWDT: Mutex>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + wdt0.disable(); + wdt1.disable(); + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + + rtc.rwdt.start(2000u64.millis()); + rtc.rwdt.listen(); + + interrupt::enable( + peripherals::Interrupt::LP_WDT, + interrupt::Priority::Priority1, + ) + .unwrap(); + + critical_section::with(|cs| RWDT.borrow_ref_mut(cs).replace(rtc.rwdt)); + + unsafe { + riscv::interrupt::enable(); + } + + loop {} +} + +#[interrupt] +fn LP_WDT() { + critical_section::with(|cs| { + esp_println::println!("RWDT Interrupt"); + + let mut rwdt = RWDT.borrow_ref_mut(cs); + let rwdt = rwdt.as_mut().unwrap(); + + rwdt.clear_interrupt(); + + esp_println::println!("Restarting in 5 seconds..."); + + rwdt.start(5000u64.millis()); + rwdt.unlisten(); + }); +} diff --git a/esp32c6-hal/examples/serial_interrupts.rs b/esp32c6-hal/examples/serial_interrupts.rs new file mode 100644 index 00000000000..842630497c4 --- /dev/null +++ b/esp32c6-hal/examples/serial_interrupts.rs @@ -0,0 +1,104 @@ +//! This shows some of the interrupts that can be generated by UART/Serial. +//! Use a proper serial terminal to connect to the board (espmonitor and +//! espflash won't work) + +#![no_std] +#![no_main] + +use core::{cell::RefCell, fmt::Write}; + +use critical_section::Mutex; +use esp32c6_hal::{ + clock::ClockControl, + interrupt, + peripherals::{self, Peripherals, UART0}, + prelude::*, + riscv, + timer::TimerGroup, + uart::config::AtCmdConfig, + Cpu, + Rtc, + Uart, +}; +use esp_backtrace as _; +use nb::block; + +static SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let mut serial0 = Uart::new(peripherals.UART0); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + serial0.set_at_cmd(AtCmdConfig::new(None, None, None, b'#', None)); + serial0.set_rx_fifo_full_threshold(30); + serial0.listen_at_cmd(); + serial0.listen_rx_fifo_full(); + + timer0.start(1u64.secs()); + + critical_section::with(|cs| SERIAL.borrow_ref_mut(cs).replace(serial0)); + + interrupt::enable( + peripherals::Interrupt::UART0, + interrupt::Priority::Priority1, + ) + .unwrap(); + interrupt::set_kind( + Cpu::ProCpu, + interrupt::CpuInterrupt::Interrupt1, // Interrupt 1 handles priority one interrupts + interrupt::InterruptKind::Edge, + ); + + unsafe { + riscv::interrupt::enable(); + } + + loop { + critical_section::with(|cs| { + writeln!(SERIAL.borrow_ref_mut(cs).as_mut().unwrap(), "Hello World! Send a single `#` character or send at least 30 characters and see the interrupts trigger.").ok(); + }); + + block!(timer0.wait()).unwrap(); + } +} + +#[interrupt] +fn UART0() { + critical_section::with(|cs| { + let mut serial = SERIAL.borrow_ref_mut(cs); + let serial = serial.as_mut().unwrap(); + + let mut cnt = 0; + while let nb::Result::Ok(_c) = serial.read() { + cnt += 1; + } + writeln!(serial, "Read {} bytes", cnt,).ok(); + + writeln!( + serial, + "Interrupt AT-CMD: {} RX-FIFO-FULL: {}", + serial.at_cmd_interrupt_set(), + serial.rx_fifo_full_interrupt_set(), + ) + .ok(); + + serial.reset_at_cmd_interrupt(); + serial.reset_rx_fifo_full_interrupt(); + }); +} diff --git a/esp32c6-hal/examples/sha.rs b/esp32c6-hal/examples/sha.rs new file mode 100644 index 00000000000..fcd6e17d3c7 --- /dev/null +++ b/esp32c6-hal/examples/sha.rs @@ -0,0 +1,80 @@ +//! Demonstrates the use of the SHA peripheral and compares the speed of +//! hardware-accelerated and pure software hashing. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + peripherals::Peripherals, + prelude::*, + sha::{Sha, ShaMode}, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; +use nb::block; +use sha2::{Digest, Sha256}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + + // Disable MWDT and RWDT (Watchdog) flash boot protection + wdt.disable(); + rtc.rwdt.disable(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let mut remaining = source_data.clone(); + let mut hasher = Sha::new(peripherals.SHA, ShaMode::SHA256); + + // Short hashes can be created by decreasing the output buffer to the desired + // length + let mut output = [0u8; 32]; + + // let pre_calc = xtensa_lx::timer::get_cycle_count(); + // The hardware implementation takes a subslice of the input, and returns the + // unprocessed parts The unprocessed parts can be input in the next + // iteration, you can always add more data until finish() is called. After + // finish() is called update()'s will contribute to a new hash which + // can be extracted again with finish(). + + while remaining.len() > 0 { + // Can add println to view progress, however println takes a few orders of + // magnitude longer than the Sha function itself so not useful for + // comparing processing time println!("Remaining len: {}", + // remaining.len()); + + // All the HW Sha functions are infallible so unwrap is fine to use if you use + // block! + remaining = block!(hasher.update(remaining)).unwrap(); + } + + // Finish can be called as many times as desired to get mutliple copies of the + // output. + block!(hasher.finish(output.as_mut_slice())).unwrap(); + // let post_calc = xtensa_lx::timer::get_cycle_count(); + // let hw_time = post_calc - pre_calc; + // println!("Took {} cycles", hw_time); + println!("SHA256 Hash output {:02x?}", output); + + // let pre_calc = xtensa_lx::timer::get_cycle_count(); + let mut hasher = Sha256::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + // let post_calc = xtensa_lx::timer::get_cycle_count(); + // let soft_time = post_calc - pre_calc; + // println!("Took {} cycles", soft_time); + println!("SHA256 Hash output {:02x?}", soft_result); + + // println!("HW SHA is {}x faster", soft_time/hw_time); + + loop {} +} diff --git a/esp32c6-hal/examples/spi_eh1_device_loopback.rs b/esp32c6-hal/examples/spi_eh1_device_loopback.rs new file mode 100644 index 00000000000..8f90179ec9f --- /dev/null +++ b/esp32c6-hal/examples/spi_eh1_device_loopback.rs @@ -0,0 +1,153 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO6 +//! MISO GPIO2 +//! MOSI GPIO7 +//! CS 1 GPIO3 +//! CS 2 GPIO4 +//! CS 3 GPIO5 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use embedded_hal_1::spi::SpiDevice; +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiBusController, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::{print, println}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + + let spi_controller = SpiBusController::from_spi(Spi::new_no_cs( + peripherals.SPI2, + sclk, + mosi, + miso, + 1000u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + )); + let mut spi_device_1 = spi_controller.add_device(io.pins.gpio3); + let mut spi_device_2 = spi_controller.add_device(io.pins.gpio4); + let mut spi_device_3 = spi_controller.add_device(io.pins.gpio5); + + let mut delay = Delay::new(&clocks); + println!("=== SPI example with embedded-hal-1 traits ==="); + + loop { + // --- Symmetric transfer (Read as much as we write) --- + print!("Starting symmetric transfer..."); + let write = [0xde, 0xad, 0xbe, 0xef]; + let mut read: [u8; 4] = [0x00u8; 4]; + + spi_device_1.transfer(&mut read[..], &write[..]).unwrap(); + assert_eq!(write, read); + spi_device_2.transfer(&mut read[..], &write[..]).unwrap(); + spi_device_3.transfer(&mut read[..], &write[..]).unwrap(); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Asymmetric transfer (Read more than we write) --- + print!("Starting asymetric transfer (read > write)..."); + let mut read: [u8; 4] = [0x00; 4]; + + spi_device_1 + .transfer(&mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + assert_eq!(write[0], read[0]); + assert_eq!(read[2], 0x00u8); + spi_device_2 + .transfer(&mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + spi_device_3 + .transfer(&mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer --- + // Only your RAM is the limit! + print!("Starting huge transfer..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + let mut read = [0x00u8; 4096]; + + spi_device_1 + .transfer(&mut read[..], &write[..]) + .expect("Huge transfer failed"); + assert_eq!(write, read); + spi_device_2 + .transfer(&mut read[..], &write[..]) + .expect("Huge transfer failed"); + spi_device_3 + .transfer(&mut read[..], &write[..]) + .expect("Huge transfer failed"); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer in-place (No additional allocation + // needed) --- + print!("Starting huge transfer (in-place)..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + + spi_device_1 + .transfer_in_place(&mut write[..]) + .expect("Huge transfer failed"); + for byte in 0..write.len() { + assert_eq!(write[byte], byte as u8); + } + spi_device_2 + .transfer_in_place(&mut write[..]) + .expect("Huge transfer failed"); + spi_device_3 + .transfer_in_place(&mut write[..]) + .expect("Huge transfer failed"); + println!(" SUCCESS"); + delay.delay_ms(250u32); + } +} diff --git a/esp32c6-hal/examples/spi_eh1_loopback.rs b/esp32c6-hal/examples/spi_eh1_loopback.rs new file mode 100644 index 00000000000..22bdcefe4f6 --- /dev/null +++ b/esp32c6-hal/examples/spi_eh1_loopback.rs @@ -0,0 +1,125 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO6 +//! MISO GPIO2 +//! MOSI GPIO7 +//! CS GPIO10 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use embedded_hal_1::spi::SpiBus; +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::{print, println}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + let cs = io.pins.gpio10; + + let mut spi = Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 1000u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ); + + let mut delay = Delay::new(&clocks); + println!("=== SPI example with embedded-hal-1 traits ==="); + + loop { + // --- Symmetric transfer (Read as much as we write) --- + print!("Starting symmetric transfer..."); + let write = [0xde, 0xad, 0xbe, 0xef]; + let mut read: [u8; 4] = [0x00u8; 4]; + + SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Symmetric transfer failed"); + assert_eq!(write, read); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Asymmetric transfer (Read more than we write) --- + print!("Starting asymetric transfer (read > write)..."); + let mut read: [u8; 4] = [0x00; 4]; + + SpiBus::transfer(&mut spi, &mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + assert_eq!(write[0], read[0]); + assert_eq!(read[2], 0x00u8); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer --- + // Only your RAM is the limit! + print!("Starting huge transfer..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + let mut read = [0x00u8; 4096]; + + SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Huge transfer failed"); + assert_eq!(write, read); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer in-place (No additional allocation + // needed) --- + print!("Starting huge transfer (in-place)..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + + SpiBus::transfer_in_place(&mut spi, &mut write[..]).expect("Huge transfer failed"); + for byte in 0..write.len() { + assert_eq!(write[byte], byte as u8); + } + println!(" SUCCESS"); + delay.delay_ms(250u32); + } +} diff --git a/esp32c6-hal/examples/spi_loopback.rs b/esp32c6-hal/examples/spi_loopback.rs new file mode 100644 index 00000000000..2f3615d2fab --- /dev/null +++ b/esp32c6-hal/examples/spi_loopback.rs @@ -0,0 +1,79 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO6 +//! MISO GPIO2 +//! MOSI GPIO7 +//! CS GPIO10 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + let cs = io.pins.gpio10; + + let mut spi = Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ); + + let mut delay = Delay::new(&clocks); + + loop { + let mut data = [0xde, 0xca, 0xfb, 0xad]; + spi.transfer(&mut data).unwrap(); + println!("{:x?}", data); + + delay.delay_ms(250u32); + } +} diff --git a/esp32c6-hal/examples/spi_loopback_dma.rs b/esp32c6-hal/examples/spi_loopback_dma.rs new file mode 100644 index 00000000000..059ffd45c34 --- /dev/null +++ b/esp32c6-hal/examples/spi_loopback_dma.rs @@ -0,0 +1,123 @@ +//! SPI loopback test using DMA +//! +//! Folowing pins are used: +//! SCLK GPIO6 +//! MISO GPIO2 +//! MOSI GPIO7 +//! CS GPIO10 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + let cs = io.pins.gpio10; + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let mut spi = Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + &mut descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + )); + + let mut delay = Delay::new(&clocks); + + // DMA buffer require a static life-time + let mut send = buffer1(); + let mut receive = buffer2(); + let mut i = 0; + + for (i, v) in send.iter_mut().enumerate() { + *v = (i % 255) as u8; + } + + loop { + send[0] = i; + send[send.len() - 1] = i; + i = i.wrapping_add(1); + + let transfer = spi.dma_transfer(send, receive).unwrap(); + // here we could do something else while DMA transfer is in progress + // the buffers and spi is moved into the transfer and we can get it back via + // `wait` + (receive, send, spi) = transfer.wait(); + println!( + "{:x?} .. {:x?}", + &receive[..10], + &receive[receive.len() - 10..] + ); + + delay.delay_ms(250u32); + } +} + +fn buffer1() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} + +fn buffer2() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32c6-hal/examples/systimer.rs b/esp32c6-hal/examples/systimer.rs new file mode 100644 index 00000000000..d252594f2c0 --- /dev/null +++ b/esp32c6-hal/examples/systimer.rs @@ -0,0 +1,130 @@ +//! This shows how to use the SYSTIMER peripheral including interrupts. +//! It's an additional timer besides the TIMG peripherals. + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp32c6_hal::{ + clock::ClockControl, + interrupt, + interrupt::Priority, + peripherals::{self, Peripherals}, + prelude::*, + systimer::{Alarm, Periodic, SystemTimer, Target}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +static ALARM0: Mutex>>> = Mutex::new(RefCell::new(None)); +static ALARM1: Mutex>>> = Mutex::new(RefCell::new(None)); +static ALARM2: Mutex>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let syst = SystemTimer::new(peripherals.SYSTIMER); + + println!("SYSTIMER Current value = {}", SystemTimer::now()); + + let alarm0 = syst.alarm0.into_periodic(); + alarm0.set_period(1u32.Hz()); + alarm0.clear_interrupt(); + alarm0.interrupt_enable(true); + + let alarm1 = syst.alarm1; + alarm1.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 2)); + alarm1.interrupt_enable(true); + + let alarm2 = syst.alarm2; + alarm2.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 3)); + alarm2.interrupt_enable(true); + + critical_section::with(|cs| { + ALARM0.borrow_ref_mut(cs).replace(alarm0); + ALARM1.borrow_ref_mut(cs).replace(alarm1); + ALARM2.borrow_ref_mut(cs).replace(alarm2); + }); + + interrupt::enable( + peripherals::Interrupt::SYSTIMER_TARGET0, + Priority::Priority1, + ) + .unwrap(); + interrupt::enable( + peripherals::Interrupt::SYSTIMER_TARGET1, + Priority::Priority2, + ) + .unwrap(); + interrupt::enable( + peripherals::Interrupt::SYSTIMER_TARGET2, + Priority::Priority2, + ) + .unwrap(); + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(&clocks); + + loop { + delay.delay_ms(10000u32); + } +} + +#[interrupt] +fn SYSTIMER_TARGET0() { + println!("Interrupt lvl1 (alarm0)"); + critical_section::with(|cs| { + ALARM0 + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); +} + +#[interrupt] +fn SYSTIMER_TARGET1() { + println!("Interrupt lvl2 (alarm1)"); + critical_section::with(|cs| { + ALARM1 + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); +} + +#[interrupt] +fn SYSTIMER_TARGET2() { + println!("Interrupt lvl2 (alarm2)"); + critical_section::with(|cs| { + ALARM2 + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); +} diff --git a/esp32c6-hal/examples/timer_interrupt.rs b/esp32c6-hal/examples/timer_interrupt.rs new file mode 100644 index 00000000000..048c44b89c2 --- /dev/null +++ b/esp32c6-hal/examples/timer_interrupt.rs @@ -0,0 +1,102 @@ +//! This shows how to use the TIMG peripheral interrupts. +//! There is TIMG0 and TIMG1 each of them containing a general purpose timer and +//! a watchdog timer. + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp32c6_hal::{ + clock::ClockControl, + interrupt, + peripherals::{self, Peripherals, TIMG0, TIMG1}, + prelude::*, + riscv, + timer::{Timer, Timer0, TimerGroup}, + Rtc, +}; +use esp_backtrace as _; + +static TIMER0: Mutex>>>> = Mutex::new(RefCell::new(None)); +static TIMER1: Mutex>>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C6, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut timer1 = timer_group1.timer0; + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + interrupt::enable( + peripherals::Interrupt::TG0_T0_LEVEL, + interrupt::Priority::Priority2, + ) + .unwrap(); + + timer0.start(500u64.millis()); + + timer0.listen(); + + interrupt::enable( + peripherals::Interrupt::TG1_T0_LEVEL, + interrupt::Priority::Priority2, + ) + .unwrap(); + + timer1.start(1u64.secs()); + timer1.listen(); + + critical_section::with(|cs| { + TIMER0.borrow_ref_mut(cs).replace(timer0); + TIMER1.borrow_ref_mut(cs).replace(timer1); + }); + + unsafe { + riscv::interrupt::enable(); + } + + loop {} +} + +#[interrupt] +fn TG0_T0_LEVEL() { + critical_section::with(|cs| { + esp_println::println!("Interrupt 1"); + + let mut timer0 = TIMER0.borrow_ref_mut(cs); + let timer0 = timer0.as_mut().unwrap(); + + timer0.clear_interrupt(); + timer0.start(500u64.millis()); + }); +} + +#[interrupt] +fn TG1_T0_LEVEL() { + critical_section::with(|cs| { + esp_println::println!("Interrupt 11"); + + let mut timer1 = TIMER1.borrow_ref_mut(cs); + let timer1 = timer1.as_mut().unwrap(); + + timer1.clear_interrupt(); + timer1.start(1u64.secs()); + }); +} diff --git a/esp32c6-hal/examples/usb_serial_jtag.rs b/esp32c6-hal/examples/usb_serial_jtag.rs new file mode 100644 index 00000000000..c13607194bd --- /dev/null +++ b/esp32c6-hal/examples/usb_serial_jtag.rs @@ -0,0 +1,91 @@ +//! This shows how to output text via USB Serial/JTAG. +//! You need to connect via the Serial/JTAG interface to see any output. +//! Most dev-kits use a USB-UART-bridge - in that case you won't see any output. + +#![no_std] +#![no_main] + +use core::{cell::RefCell, fmt::Write}; + +use critical_section::Mutex; +use esp32c6_hal::{ + clock::ClockControl, + interrupt, + peripherals::{self, Peripherals, USB_DEVICE}, + prelude::*, + riscv, + timer::TimerGroup, + Cpu, + Rtc, + UsbSerialJtag, +}; +use esp_backtrace as _; +use nb::block; + +static USB_SERIAL: Mutex>>> = + Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let mut usb_serial = UsbSerialJtag::new(peripherals.USB_DEVICE); + + usb_serial.listen_rx_packet_recv_interrupt(); + + timer0.start(1u64.secs()); + + critical_section::with(|cs| USB_SERIAL.borrow_ref_mut(cs).replace(usb_serial)); + + interrupt::enable(peripherals::Interrupt::USB, interrupt::Priority::Priority1).unwrap(); + + interrupt::set_kind( + Cpu::ProCpu, + interrupt::CpuInterrupt::Interrupt1, + interrupt::InterruptKind::Edge, + ); + + unsafe { + riscv::interrupt::enable(); + } + + loop { + critical_section::with(|cs| { + writeln!( + USB_SERIAL.borrow_ref_mut(cs).as_mut().unwrap(), + "Hello world!" + ) + .ok(); + }); + + block!(timer0.wait()).unwrap(); + } +} + +#[interrupt] +fn USB() { + critical_section::with(|cs| { + let mut usb_serial = USB_SERIAL.borrow_ref_mut(cs); + let usb_serial = usb_serial.as_mut().unwrap(); + writeln!(usb_serial, "USB serial interrupt").unwrap(); + while let nb::Result::Ok(c) = usb_serial.read_byte() { + writeln!(usb_serial, "Read byte: {:02x}", c).unwrap(); + } + usb_serial.reset_rx_packet_recv_interrupt(); + }); +} diff --git a/esp32c6-hal/examples/watchdog.rs b/esp32c6-hal/examples/watchdog.rs new file mode 100644 index 00000000000..92221f6751a --- /dev/null +++ b/esp32c6-hal/examples/watchdog.rs @@ -0,0 +1,45 @@ +//! This demos the watchdog timer. +//! Basically the same as `hello_world` but if you remove the call to +//! `wdt.feed()` the watchdog will reset the system. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; +use nb::block; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.start(2u64.secs()); + wdt1.disable(); + + timer0.start(1u64.secs()); + + loop { + wdt0.feed(); + println!("Hello world!"); + block!(timer0.wait()).unwrap(); + } +} diff --git a/esp32c6-hal/ld/bl-esp32c6-memory.x b/esp32c6-hal/ld/bl-esp32c6-memory.x new file mode 100644 index 00000000000..d899e977cd1 --- /dev/null +++ b/esp32c6-hal/ld/bl-esp32c6-memory.x @@ -0,0 +1,33 @@ +MEMORY +{ + /* MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x42800000, 0x43000000, "DROM"], + [0x40800000, 0x40880000, "RAM"], + [0x40800000, 0x40880000, "BYTE_ACCESSIBLE"], + [0x4004AC00, 0x40050000, "DROM_MASK"], + [0x40000000, 0x4004AC00, "ROM_MASK"], + [0x42000000, 0x42800000, "ROM"], + [0x40800000, 0x40880000, "RAM"], + [0x50000000, 0x50004000, "RTC_RAM"], + [0x50000000, 0x50004000, "RTC_RAM"], + [0x600FE000, 0x60100000, "MEM_INTERNAL2"], + ] */ + + /* 512K of on soc RAM, 32K reserved for cache */ + ICACHE : ORIGIN = 0x40800000, LENGTH = 32K + /* Instruction and Data RAM */ + RAM : ORIGIN = 0x40800000 + 32K, LENGTH = 512K - 32K + + /* External flash */ + /* Instruction and Data ROM */ + ROM : ORIGIN = 0x42000000 + 0x20, LENGTH = 0x400000 - 0x20 + + /* RTC fast memory (executable). Persists over deep sleep. */ + RTC_FAST : ORIGIN = 0x50000000, LENGTH = 16K /*- ESP_BOOTLOADER_RESERVE_RTC*/ +} + +REGION_ALIAS("REGION_TEXT", ROM); +REGION_ALIAS("REGION_RODATA", ROM); + +REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); diff --git a/esp32c6-hal/ld/bl-linkall.x b/esp32c6-hal/ld/bl-linkall.x new file mode 100644 index 00000000000..e604dc6f8f8 --- /dev/null +++ b/esp32c6-hal/ld/bl-linkall.x @@ -0,0 +1,4 @@ +INCLUDE "memory.x" +INCLUDE "bl-riscv-link.x" +INCLUDE "hal-defaults.x" +INCLUDE "rom-functions.x" diff --git a/esp32c6-hal/ld/bl-riscv-link.x b/esp32c6-hal/ld/bl-riscv-link.x new file mode 100644 index 00000000000..82e1b39629e --- /dev/null +++ b/esp32c6-hal/ld/bl-riscv-link.x @@ -0,0 +1,241 @@ +ENTRY(_start) + +PROVIDE(_stext = ORIGIN(REGION_TEXT)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(UserSoft = DefaultHandler); +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(UserTimer = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(UserExternal = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text _stext : + { + _stext = .; + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + KEEP(*(.text.abort)); + . = ALIGN(4); + KEEP(*(.trap)); + KEEP(*(.trap.rust)); + + *(.text .text.*); + _etext = .; + } > ROM + + /** + * Bootloader really wants to have separate segments for ROTEXT and RODATA + * Thus, we need to force a gap here. + */ + .text_gap (NOLOAD): { + . = . + 4; + . = ALIGN(4) + 0x20; + } > ROM + + .rodata : ALIGN(4) + { + _srodata = .; + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + _erodata = .; + } > ROM + + .rwtext : ALIGN(4) { + _irwtext = LOADADDR(.rwtext); + _srwtext = .; + *(.rwtext); + + *(.iram1) + *(.iram1.*) + + *(.wifi0iram .wifi0iram.*) + *(.wifirxiram .wifirxiram.*) + *(.wifislpiram .wifislpiram.*) + *(.wifislprxiram .wifislprxiram.*) + + . = ALIGN(4); + _erwtext = .; + } > RAM + + .data : ALIGN(8) + { + _sidata = LOADADDR(.data); + _sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + *(.data1) + + . = ALIGN(8); + _edata = .; + } > RAM + + .bss (NOLOAD) : + { + . = ALIGN(8); + _sbss = .; + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.sbss .sbss.* .bss .bss.*); + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + + . = ALIGN(8); + _ebss = .; + } > RAM + + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); + __euninit = .; + } > RAM + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + _heap_start = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + .rtc_fast.text : ALIGN(4) { + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + } > REGION_RTC_FAST AT > REGION_RODATA + + .rtc_fast.data : ALIGN(4) + { + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + _rtc_fast_data_end = ABSOLUTE(.); + } > REGION_RTC_FAST AT > REGION_RODATA + + .rtc_fast.bss (NOLOAD) : ALIGN(4) + { + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + _rtc_fast_bss_end = ABSOLUTE(.); + } > REGION_RTC_FAST + + .rtc_fast.noinit (NOLOAD) : ALIGN(4) + { + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > REGION_RTC_FAST + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 4 == 0, " +ERROR(riscv-rt): the start of the RAM must be 4-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(_sstack % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " +BUG(riscv-rt): .data is not 4-byte aligned"); + +ASSERT(_sidata % 4 == 0, " +BUG(riscv-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, " +BUG(riscv-rt): .bss is not 4-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " +ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. +Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/esp32c6-hal/ld/db-esp32c6-link.x b/esp32c6-hal/ld/db-esp32c6-link.x new file mode 100644 index 00000000000..b2e740b39dc --- /dev/null +++ b/esp32c6-hal/ld/db-esp32c6-link.x @@ -0,0 +1,14 @@ +INCLUDE memory.x + +SECTIONS +{ + .header : AT(0) + { + LONG(0xaedb041d) + LONG(0xaedb041d) + } > ROM +} + +_stext = ORIGIN(ROM) + 8; + +INCLUDE riscv-link.x diff --git a/esp32c6-hal/ld/db-esp32c6-memory.x b/esp32c6-hal/ld/db-esp32c6-memory.x new file mode 100644 index 00000000000..b4cbf63cc93 --- /dev/null +++ b/esp32c6-hal/ld/db-esp32c6-memory.x @@ -0,0 +1,38 @@ +MEMORY +{ + /* MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x42800000, 0x43000000, "DROM"], + [0x40800000, 0x40880000, "DRAM"], + [0x40800000, 0x40880000, "BYTE_ACCESSIBLE"], + [0x4004AC00, 0x40050000, "DROM_MASK"], + [0x40000000, 0x4004AC00, "IROM_MASK"], + [0x42000000, 0x42800000, "IROM"], + [0x40800000, 0x40880000, "IRAM"], + [0x50000000, 0x50004000, "RTC_IRAM"], + [0x50000000, 0x50004000, "RTC_DRAM"], + [0x600FE000, 0x60100000, "MEM_INTERNAL2"], + ] */ + + /* 512K of on soc RAM, 32K reserved for cache */ + ICACHE : ORIGIN = 0x40800000, LENGTH = 32K + + RAM : ORIGIN = 0x40800000 + 32K, LENGTH = 512K - 32K + + /* External flash */ + ROM : ORIGIN = 0x42000000, LENGTH = 0x400000 + + /* RTC fast memory (executable). Persists over deep sleep. */ + RTC_FAST : ORIGIN = 0x50000000, LENGTH = 16K /*- ESP_BOOTLOADER_RESERVE_RTC*/ +} + +REGION_ALIAS("REGION_TEXT", ROM); +REGION_ALIAS("REGION_RODATA", ROM); + +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); + +REGION_ALIAS("REGION_RWTEXT", RAM); +REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); diff --git a/esp32c6-hal/ld/db-linkall.x b/esp32c6-hal/ld/db-linkall.x new file mode 100644 index 00000000000..99a75142965 --- /dev/null +++ b/esp32c6-hal/ld/db-linkall.x @@ -0,0 +1,3 @@ +INCLUDE "esp32c6-link.x" +INCLUDE "hal-defaults.x" +INCLUDE "rom-functions.x" diff --git a/esp32c6-hal/ld/db-riscv-link.x b/esp32c6-hal/ld/db-riscv-link.x new file mode 100644 index 00000000000..7df2124aafa --- /dev/null +++ b/esp32c6-hal/ld/db-riscv-link.x @@ -0,0 +1,235 @@ +ENTRY(_start) + +PROVIDE(_stext = ORIGIN(REGION_TEXT)); +PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(UserSoft = DefaultHandler); +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(UserTimer = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(UserExternal = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > REGION_TEXT + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + KEEP(*(.text.abort)); + . = ALIGN(4); + KEEP(*(.trap)); + KEEP(*(.trap.rust)); + + *(.text .text.*); + _etext = .; + } > REGION_TEXT + + _text_size = _etext - _stext + 8; + .rodata ORIGIN(ROM) + _text_size : AT(_text_size) + { + _srodata = .; + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + _erodata = .; + } > REGION_RODATA + + _rodata_size = _erodata - _srodata + 8; + .data ORIGIN(RAM) : AT(_text_size + _rodata_size) + { + _sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(4); + _edata = .; + } > REGION_DATA + + _data_size = _edata - _sdata + 8; + .rwtext ORIGIN(REGION_RWTEXT) + _data_size : AT(_text_size + _rodata_size + _data_size){ + _srwtext = .; + *(.rwtext); + . = ALIGN(4); + _erwtext = .; + } > REGION_RWTEXT + _rwtext_size = _erwtext - _srwtext + 8; + + .rtc_fast.text : AT(_text_size + _rodata_size + _data_size + _rwtext_size) { + _srtc_fast_text = .; + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + . = ALIGN(4); + _ertc_fast_text = .; + } > REGION_RTC_FAST + _fast_text_size = _ertc_fast_text - _srtc_fast_text + 8; + + .rtc_fast.data : AT(_text_size + _rodata_size + _data_size + _rwtext_size + _fast_text_size) + { + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + . = ALIGN(4); + _rtc_fast_data_end = ABSOLUTE(.); + } > REGION_RTC_FAST + _rtc_fast_data_size = _rtc_fast_data_end - _rtc_fast_data_start + 8; + + .rtc_fast.bss (NOLOAD) : ALIGN(4) + { + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + . = ALIGN(4); + _rtc_fast_bss_end = ABSOLUTE(.); + } > REGION_RTC_FAST + + .rtc_fast.noinit (NOLOAD) : ALIGN(4) + { + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > REGION_RTC_FAST + + .bss (NOLOAD) : + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(4); + _ebss = .; + } > REGION_BSS + + /* ### .uninit */ + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); + __euninit = .; + } > REGION_BSS + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > REGION_HEAP + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > REGION_STACK + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(_sidata = _erodata + 8); +PROVIDE(_irwtext = ORIGIN(ROM) + _text_size + _rodata_size + _data_size); +PROVIDE(_irtc_fast_text = ORIGIN(ROM) + _text_size + _rodata_size + _data_size + _rwtext_size); +PROVIDE(_irtc_fast_data = ORIGIN(ROM) + _text_size + _rodata_size + _data_size + _rwtext_size + _fast_text_size); + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_DATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_STACK) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " +BUG(riscv-rt): .data is not 4-byte aligned"); + +ASSERT(_sidata % 4 == 0, " +BUG(riscv-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, " +BUG(riscv-rt): .bss is not 4-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " +ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. +Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/esp32c6-hal/ld/hal-defaults.x b/esp32c6-hal/ld/hal-defaults.x new file mode 100644 index 00000000000..85c38b127c7 --- /dev/null +++ b/esp32c6-hal/ld/hal-defaults.x @@ -0,0 +1,34 @@ +PROVIDE(interrupt0 = DefaultHandler); +PROVIDE(interrupt1 = DefaultHandler); +PROVIDE(interrupt2 = DefaultHandler); +PROVIDE(interrupt3 = DefaultHandler); +PROVIDE(interrupt4 = DefaultHandler); +PROVIDE(interrupt5 = DefaultHandler); +PROVIDE(interrupt6 = DefaultHandler); +PROVIDE(interrupt7 = DefaultHandler); +PROVIDE(interrupt8 = DefaultHandler); +PROVIDE(interrupt9 = DefaultHandler); +PROVIDE(interrupt10 = DefaultHandler); +PROVIDE(interrupt11 = DefaultHandler); +PROVIDE(interrupt12 = DefaultHandler); +PROVIDE(interrupt13 = DefaultHandler); +PROVIDE(interrupt14 = DefaultHandler); +PROVIDE(interrupt15 = DefaultHandler); +PROVIDE(interrupt16 = DefaultHandler); +PROVIDE(interrupt17 = DefaultHandler); +PROVIDE(interrupt18 = DefaultHandler); +PROVIDE(interrupt19 = DefaultHandler); +PROVIDE(interrupt20 = DefaultHandler); +PROVIDE(interrupt21 = DefaultHandler); +PROVIDE(interrupt22 = DefaultHandler); +PROVIDE(interrupt23 = DefaultHandler); +PROVIDE(interrupt24 = DefaultHandler); +PROVIDE(interrupt25 = DefaultHandler); +PROVIDE(interrupt26 = DefaultHandler); +PROVIDE(interrupt27 = DefaultHandler); +PROVIDE(interrupt28 = DefaultHandler); +PROVIDE(interrupt29 = DefaultHandler); +PROVIDE(interrupt30 = DefaultHandler); +PROVIDE(interrupt31 = DefaultHandler); + +INCLUDE "device.x" diff --git a/esp32c6-hal/ld/rom-functions.x b/esp32c6-hal/ld/rom-functions.x new file mode 100644 index 00000000000..60f3c118301 --- /dev/null +++ b/esp32c6-hal/ld/rom-functions.x @@ -0,0 +1,12 @@ +ets_printf = 0x40000028; +ets_update_cpu_frequency = ets_update_cpu_frequency_rom; +PROVIDE(esp_rom_printf = ets_printf); +PROVIDE(cache_invalidate_icache_all = 0x4000064c); +PROVIDE(cache_suspend_icache = 0x40000698); +PROVIDE(cache_resume_icache = 0x4000069c); +/* TODO PROVIDE(cache_ibus_mmu_set = 0x40000560); */ +/* TODO PROVIDE(cache_dbus_mmu_set = 0x40000564); */ +PROVIDE(ets_delay_us = 0x40000040); +PROVIDE(ets_update_cpu_frequency_rom = 0x40000048); +PROVIDE(rtc_get_reset_reason = 0x40000018); +ets_update_cpu_frequency = 0x40000048; diff --git a/esp32c6-hal/src/lib.rs b/esp32c6-hal/src/lib.rs new file mode 100644 index 00000000000..4b517a16307 --- /dev/null +++ b/esp32c6-hal/src/lib.rs @@ -0,0 +1,112 @@ +#![no_std] + +pub use embedded_hal as ehal; +#[cfg(feature = "embassy")] +pub use esp_hal_common::embassy; +pub use esp_hal_common::{ + aes, + analog::adc::implementation as adc, + clock, + dma, + dma::gdma, + efuse, + entry, + gpio, + i2c, + i2s, + interrupt, + ledc, + macros, + mcpwm, + pcnt, + peripherals, + prelude, + pulse_control, + riscv, + sha, + spi, + systimer, + timer, + trapframe, + twai, + uart, + utils, + Cpu, + Delay, + PulseControl, + Rtc, + Rwdt, + Uart, + UsbSerialJtag, +}; + +pub use self::gpio::IO; + +/// Common module for analog functions +pub mod analog { + pub use esp_hal_common::analog::{AvailableAnalog, SarAdcExt}; +} + +extern "C" { + // Boundaries of the .iram section + static mut _srwtext: u32; + static mut _erwtext: u32; + static mut _irwtext: u32; + + // Boundaries of the .bss section + static mut _ebss: u32; + static mut _sbss: u32; + + // Boundaries of the rtc .bss section + static mut _rtc_fast_bss_start: u32; + static mut _rtc_fast_bss_end: u32; + + // Boundaries of the .rtc_fast.text section + static mut _srtc_fast_text: u32; + static mut _ertc_fast_text: u32; + static mut _irtc_fast_text: u32; + + // Boundaries of the .rtc_fast.data section + static mut _rtc_fast_data_start: u32; + static mut _rtc_fast_data_end: u32; + static mut _irtc_fast_data: u32; +} + +#[cfg(feature = "direct-boot")] +#[doc(hidden)] +#[esp_hal_common::esp_riscv_rt::pre_init] +unsafe fn init() { + r0::init_data(&mut _srwtext, &mut _erwtext, &_irwtext); + + r0::init_data( + &mut _rtc_fast_data_start, + &mut _rtc_fast_data_end, + &_irtc_fast_data, + ); + + r0::init_data(&mut _srtc_fast_text, &mut _ertc_fast_text, &_irtc_fast_text); + + esp_hal_common::disable_apm_filter(); +} + +#[allow(unreachable_code)] +#[export_name = "_mp_hook"] +#[doc(hidden)] +pub fn mp_hook() -> bool { + unsafe { + r0::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end); + } + + #[cfg(feature = "direct-boot")] + return true; + + // no init data when using normal boot - but we need to zero out BSS + unsafe { + r0::zero_bss(&mut _sbss, &mut _ebss); + } + + false +} + +#[no_mangle] +extern "C" fn EspDefaultHandler(_interrupt: peripherals::Interrupt) {}