From 47821e6b3b6a6d811041856f020c42f0bf6083bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Tue, 10 Oct 2023 16:32:52 +0200 Subject: [PATCH] Add ULP RISC-V HAL (#840) --- .github/workflows/ci.yml | 156 ++++++++++----- CHANGELOG.md | 1 + esp-hal-common/src/gpio.rs | 182 ++++++++++++++++++ esp-hal-procmacros/Cargo.toml | 4 +- esp-hal-procmacros/src/lib.rs | 51 ++++- esp32c6-lp-hal-procmacros/Cargo.toml | 33 ---- esp32c6-lp-hal/Cargo.toml | 2 +- esp32c6-lp-hal/src/prelude.rs | 2 +- esp32s2-hal/examples/ulp_riscv_core_basic.rs | 58 +++--- esp32s3-hal/examples/ulp_riscv_core_basic.rs | 58 +++--- lp-hal-procmacros/Cargo.toml | 36 ++++ .../src/lib.rs | 6 + ulp-riscv-hal/.cargo/config.toml | 11 ++ ulp-riscv-hal/CHANGELOG.md | 22 +++ ulp-riscv-hal/Cargo.toml | 39 ++++ ulp-riscv-hal/README.md | 53 +++++ ulp-riscv-hal/build.rs | 17 ++ ulp-riscv-hal/examples/blinky.rs | 35 ++++ ulp-riscv-hal/ld/link.x | 53 +++++ ulp-riscv-hal/src/delay.rs | 62 ++++++ ulp-riscv-hal/src/gpio.rs | 122 ++++++++++++ ulp-riscv-hal/src/lib.rs | 97 ++++++++++ ulp-riscv-hal/src/prelude.rs | 15 ++ 23 files changed, 952 insertions(+), 163 deletions(-) delete mode 100644 esp32c6-lp-hal-procmacros/Cargo.toml create mode 100644 lp-hal-procmacros/Cargo.toml rename {esp32c6-lp-hal-procmacros => lp-hal-procmacros}/src/lib.rs (93%) create mode 100644 ulp-riscv-hal/.cargo/config.toml create mode 100644 ulp-riscv-hal/CHANGELOG.md create mode 100644 ulp-riscv-hal/Cargo.toml create mode 100644 ulp-riscv-hal/README.md create mode 100644 ulp-riscv-hal/build.rs create mode 100644 ulp-riscv-hal/examples/blinky.rs create mode 100644 ulp-riscv-hal/ld/link.x create mode 100644 ulp-riscv-hal/src/delay.rs create mode 100644 ulp-riscv-hal/src/gpio.rs create mode 100644 ulp-riscv-hal/src/lib.rs create mode 100644 ulp-riscv-hal/src/prelude.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4920b2c30..856f3cd20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -348,6 +348,26 @@ jobs: - name: rustdoc run: cd esp32h2-hal/ && cargo doc --features=eh1 + ulp-riscv-hal: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@v1 + with: + target: riscv32imc-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. + - name: build ulp-riscv-hal (esp32s3) + run: cd ulp-riscv-hal/ && cargo +nightly build --release --features=esp32s3 --examples + # Ensure documentation can be built + - name: rustdoc + run: cd ulp-riscv-hal/ && cargo doc --features=esp32s3 + esp32s2-hal: runs-on: ubuntu-latest @@ -358,48 +378,58 @@ jobs: default: true buildtargets: esp32s2 ldproxy: false + - uses: dtolnay/rust-toolchain@v1 + with: + target: riscv32imc-unknown-none-elf + toolchain: nightly + components: rust-src - uses: Swatinem/rust-cache@v2 + # build the ulp-riscv-hal examples first to make sure the examples which expect + # the ELF files to be present will compile + - name: build ulp-riscv-hal prerequisites + run: cd ulp-riscv-hal/ && cargo +nightly build --release --features=esp32s2 --examples + # Perform a full build initially to verify that the examples not only # build, but also link successfully. - name: check esp32s2-hal (no features) - run: cd esp32s2-hal/ && cargo build --examples + run: cd esp32s2-hal/ && cargo +esp build --examples # Subsequent steps can just check the examples instead, as we're already # confident that they link. - name: check esp32s2-hal (common features) run: | cd esp32s2-hal/ - cargo build --examples --features=eh1,ufmt,log - cargo build --examples --features=eh1,ufmt,defmt + cargo +esp build --examples --features=eh1,ufmt,log + cargo +esp build --examples --features=eh1,ufmt,defmt # FIXME: `time-systick` feature disabled for now, see 'esp32s2-hal/Cargo.toml'. # - name: check esp32s2-hal (async, systick) # run: cd esp32s2-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-systick,executor - name: check esp32s2-hal (embassy, timg0) run: | cd esp32s2-hal/ - cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0,embassy-executor-thread - cargo check --example=embassy_multiprio --features=embassy,embassy-time-timg0,embassy-executor-interrupt + cargo +esp check --example=embassy_hello_world --features=embassy,embassy-time-timg0,embassy-executor-thread + cargo +esp check --example=embassy_multiprio --features=embassy,embassy-time-timg0,embassy-executor-interrupt - name: check esp32s2-hal (embassy, timg0, async) run: | cd esp32s2-hal/ - cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_serial --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_i2c --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_i2s_read --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_i2s_sound --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_rmt_rx --features=embassy,embassy-time-timg0,async,embassy-executor-thread --release - cargo check --example=embassy_rmt_tx --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_wait --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_spi --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_serial --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_i2c --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_i2s_read --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_i2s_sound --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_rmt_rx --features=embassy,embassy-time-timg0,async,embassy-executor-thread --release + cargo +esp check --example=embassy_rmt_tx --features=embassy,embassy-time-timg0,async,embassy-executor-thread - name: check esp32s2-hal (embassy, log/defmt) run: | cd esp32s2-hal/ - cargo check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,defmt - cargo check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,log + cargo +esp check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,defmt + cargo +esp check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,log - name: check esp32s2-hal (psram) - run: cd esp32s2-hal/ && cargo check --example=psram --features=psram-2m --release # This example requires release! + run: cd esp32s2-hal/ && cargo +esp check --example=psram --features=psram-2m --release # This example requires release! # Ensure documentation can be built - name: rustdoc - run: cd esp32s2-hal/ && cargo doc --features=eh1 + run: cd esp32s2-hal/ && cargo +esp doc --features=eh1 esp32s3-hal: runs-on: ubuntu-latest @@ -411,65 +441,75 @@ jobs: default: true buildtargets: esp32s3 ldproxy: false + - uses: dtolnay/rust-toolchain@v1 + with: + target: riscv32imc-unknown-none-elf + toolchain: nightly + components: rust-src - uses: Swatinem/rust-cache@v2 + # build the ulp-riscv-hal examples first to make sure the examples which expect + # the ELF files to be present will compile + - name: build ulp-riscv-hal prerequisites + run: cd ulp-riscv-hal/ && cargo +nightly build --release --features=esp32s3 --examples + # 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 esp32s3-hal (no features) - run: cd esp32s3-hal/ && cargo build --examples + run: cd esp32s3-hal/ && cargo +esp build --examples - name: build esp32s3-hal (direct-boot) - run: cd esp32s3-hal/ && cargo build --examples --features=direct-boot + run: cd esp32s3-hal/ && cargo +esp build --examples --features=direct-boot # Subsequent steps can just check the examples instead, as we're already # confident that they link. - name: check esp32s3-hal (common features) run: | cd esp32s3-hal/ - cargo build --examples --features=eh1,ufmt,log - cargo build --examples --features=eh1,ufmt,defmt + cargo +esp build --examples --features=eh1,ufmt,log + cargo +esp build --examples --features=eh1,ufmt,defmt - name: check esp32s3-hal (embassy, timg0) run: | cd esp32s3-hal/ - cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0,embassy-executor-thread - cargo check --example=embassy_multicore --features=embassy,embassy-time-timg0,embassy-executor-thread - cargo check --example=embassy_multicore_interrupt --features=embassy,embassy-time-timg0,embassy-executor-interrupt - cargo check --example=embassy_multiprio --features=embassy,embassy-time-timg0,embassy-executor-interrupt + cargo +esp check --example=embassy_hello_world --features=embassy,embassy-time-timg0,embassy-executor-thread + cargo +esp check --example=embassy_multicore --features=embassy,embassy-time-timg0,embassy-executor-thread + cargo +esp check --example=embassy_multicore_interrupt --features=embassy,embassy-time-timg0,embassy-executor-interrupt + cargo +esp check --example=embassy_multiprio --features=embassy,embassy-time-timg0,embassy-executor-interrupt - name: check esp32s3-hal (embassy, systick) run: | cd esp32s3-hal/ - cargo check --example=embassy_hello_world --features=embassy,embassy-time-systick,embassy-executor-thread - cargo check --example=embassy_multicore --features=embassy,embassy-time-systick,embassy-executor-thread - cargo check --example=embassy_multicore_interrupt --features=embassy,embassy-time-systick,embassy-executor-interrupt - cargo check --example=embassy_multiprio --features=embassy,embassy-time-systick,embassy-executor-interrupt + cargo +esp check --example=embassy_hello_world --features=embassy,embassy-time-systick,embassy-executor-thread + cargo +esp check --example=embassy_multicore --features=embassy,embassy-time-systick,embassy-executor-thread + cargo +esp check --example=embassy_multicore_interrupt --features=embassy,embassy-time-systick,embassy-executor-interrupt + cargo +esp check --example=embassy_multiprio --features=embassy,embassy-time-systick,embassy-executor-interrupt - name: check esp32s3-hal (embassy, timg0, async) run: | cd esp32s3-hal/ - cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_serial --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_i2c --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_i2s_read --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_i2s_sound --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_rmt_rx --features=embassy,embassy-time-timg0,async,embassy-executor-thread - cargo check --example=embassy_rmt_tx --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_wait --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_spi --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_serial --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_i2c --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_i2s_read --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_i2s_sound --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_rmt_rx --features=embassy,embassy-time-timg0,async,embassy-executor-thread + cargo +esp check --example=embassy_rmt_tx --features=embassy,embassy-time-timg0,async,embassy-executor-thread - name: check esp32s3-hal (embassy, systick, async) run: | cd esp32s3-hal/ - cargo check --example=embassy_wait --features=embassy,embassy-time-systick,async,embassy-executor-thread - cargo check --example=embassy_spi --features=embassy,embassy-time-systick,async,embassy-executor-thread - cargo check --example=embassy_serial --features=embassy,embassy-time-systick,async,embassy-executor-thread - cargo check --example=embassy_i2c --features=embassy,embassy-time-systick,async,embassy-executor-thread + cargo +esp check --example=embassy_wait --features=embassy,embassy-time-systick,async,embassy-executor-thread + cargo +esp check --example=embassy_spi --features=embassy,embassy-time-systick,async,embassy-executor-thread + cargo +esp check --example=embassy_serial --features=embassy,embassy-time-systick,async,embassy-executor-thread + cargo +esp check --example=embassy_i2c --features=embassy,embassy-time-systick,async,embassy-executor-thread - name: check esp32s3-hal (octal psram and psram) run: | # This examples require release! cd esp32s3-hal/ - cargo check --example=octal_psram --features=opsram-2m --release - cargo check --example=psram --features=psram-2m --release + cargo +esp check --example=octal_psram --features=opsram-2m --release + cargo +esp check --example=psram --features=psram-2m --release - name: check esp32s3-hal (embassy, log/defmt) run: | cd esp32s3-hal/ - cargo check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,defmt - cargo check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,log + cargo +esp check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,defmt + cargo +esp check --examples --features=embassy,embassy-time-timg0,embassy-executor-interrupt,embassy-executor-thread,log # Ensure documentation can be built - name: rustdoc run: cd esp32s3-hal/ && cargo doc --features=eh1 @@ -547,24 +587,34 @@ jobs: with: ldproxy: false version: "1.67.0" + - uses: dtolnay/rust-toolchain@v1 + with: + target: riscv32imc-unknown-none-elf + toolchain: "1.67" + components: rust-src - uses: Swatinem/rust-cache@v2 + # build the ulp-riscv-hal examples first to make sure the examples which expect + # the ELF files to be present will compile + - name: build ulp-riscv-hal prerequisites + run: cd ulp-riscv-hal/ && RUSTC_BOOTSTRAP=1 cargo +1.67 build --release --features=esp32s3 --examples + # Verify the MSRV for all Xtensa chips. - name: msrv (esp32-hal) run: | cd esp32-hal/ - cargo check --features=eh1,ufmt,log - cargo check --features=defmt + cargo +esp check --features=eh1,ufmt,log + cargo +esp check --features=defmt - name: msrv (esp32s2-hal) run: | cd esp32s2-hal/ - cargo check --features=eh1,ufmt,log - cargo check --features=defmt + cargo +esp check --features=eh1,ufmt,log + cargo +esp check --features=defmt - name: msrv (esp32s3-hal) run: | cd esp32s3-hal/ - cargo check --features=eh1,ufmt,log - cargo check --features=defmt + cargo +esp check --features=eh1,ufmt,log + cargo +esp check --features=defmt # -------------------------------------------------------------------------- # Lint @@ -593,6 +643,8 @@ jobs: run: cargo +stable clippy --manifest-path=esp32c6-lp-hal/Cargo.toml -- -D warnings -A asm-sub-register - name: clippy (esp32h2-hal) run: cargo +stable clippy --manifest-path=esp32h2-hal/Cargo.toml -- -D warnings + - name: clippy (ulp-riscv-hal) + run: cargo +stable clippy --manifest-path=ulp-riscv-hal/Cargo.toml --features=esp32s3 -- -D warnings -A asm-sub-register clippy-xtensa: runs-on: ubuntu-latest @@ -652,3 +704,5 @@ jobs: 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 + - name: rustfmt (ulp-riscv-hal) + run: cargo fmt --all --manifest-path=ulp-riscv-hal/Cargo.toml -- --check diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f647983..3df2c6d5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SYSTIMER ETM functionality (#828) - Adding async support for RSA peripheral(doesn't work properly for `esp32` chip - issue will be created)(#790) - Added sleep support for ESP32-C3 with timer and GPIO wakeups (#795) +- Support for ULP-RISCV including Delay and GPIO (#840) ### Changed diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index 00f5ea02a..a7a474f95 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -1461,6 +1461,11 @@ macro_rules! rtc_pins { use crate::peripherals::RTC_IO; let rtcio = unsafe{ &*RTC_IO::ptr() }; + #[cfg(esp32s3)] + unsafe { crate::peripherals::SENS::steal() }.sar_peri_clk_gate_conf.modify(|_,w| w.iomux_clk_en().set_bit()); + #[cfg(esp32s2)] + unsafe { crate::peripherals::SENS::steal() }.sar_io_mux_conf.modify(|_,w| w.iomux_clk_gate_en().set_bit()); + // disable input paste::paste!{ rtcio.$pin_reg.modify(|_,w| unsafe {w @@ -1501,6 +1506,21 @@ macro_rules! rtc_pins { } } } + + #[cfg(not(esp32))] + paste::paste!{ + impl crate::gpio::rtc_io::IntoLowPowerPin<$pin_num> for GpioPin { + fn into_low_power(mut self) -> crate::gpio::rtc_io::LowPowerPin { + use crate::gpio::RTCPin; + + self.rtc_set_config(false, true, crate::gpio::RtcFunction::Rtc); + + crate::gpio::rtc_io::LowPowerPin { + private: core::marker::PhantomData::default(), + } + } + } + } )? }; @@ -1987,6 +2007,168 @@ pub mod etm { } } +#[cfg(all(rtc_io, not(esp32)))] +pub mod rtc_io { + //! RTC IO + //! + //! # Overview + //! + //! The hardware provides a couple of GPIO pins with low power (LP) + //! capabilities and analog functions. These pins can be controlled by + //! either IO MUX or RTC IO. + //! + //! If controlled by RTC IO, these pins will bypass IO MUX and GPIO + //! matrix for the use by ULP and peripherals in RTC system. + //! + //! When configured as RTC GPIOs, the pins can still be controlled by ULP or + //! the peripherals in RTC system during chip Deep-sleep, and wake up the + //! chip from Deep-sleep. + //! + //! # Example + //! ```no_run + //! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + //! // configure GPIO 1 as ULP output pin + //! let lp_pin = io.pins.gpio1.into_low_power().into_push_pull_output(); + //! ``` + + use core::marker::PhantomData; + + use super::{Floating, Input, Output, PullDown, PullUp, PushPull, Unknown}; + + /// A GPIO pin configured for low power operation + pub struct LowPowerPin { + pub(crate) private: PhantomData, + } + + /// Configures a pin for use as a low power pin + pub trait IntoLowPowerPin { + fn into_low_power(self) -> LowPowerPin; + } + + impl LowPowerPin { + #[doc(hidden)] + pub fn output_enable(&self, enable: bool) { + let rtc_io = unsafe { crate::peripherals::RTC_IO::steal() }; + if enable { + // TODO align PAC + #[cfg(esp32s2)] + rtc_io + .rtc_gpio_enable_w1ts + .write(|w| w.reg_rtcio_reg_gpio_enable_w1ts().variant(1 << PIN)); + + #[cfg(esp32s3)] + rtc_io + .rtc_gpio_enable_w1ts + .write(|w| w.rtc_gpio_enable_w1ts().variant(1 << PIN)); + } else { + rtc_io + .enable_w1tc + .write(|w| w.enable_w1tc().variant(1 << PIN)); + } + } + + fn input_enable(&self, enable: bool) { + get_pin_reg(PIN).modify(|_, w| w.fun_ie().bit(enable)); + } + + fn pullup_enable(&self, enable: bool) { + get_pin_reg(PIN).modify(|_, w| w.rue().bit(enable)); + } + + fn pulldown_enable(&self, enable: bool) { + get_pin_reg(PIN).modify(|_, w| w.rde().bit(enable)); + } + + #[doc(hidden)] + pub fn set_level(&mut self, level: bool) { + let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR }; + + // TODO align PACs + #[cfg(esp32s2)] + if level { + rtc_io + .rtc_gpio_out_w1ts + .write(|w| w.gpio_out_data_w1ts().variant(1 << PIN)); + } else { + rtc_io + .rtc_gpio_out_w1tc + .write(|w| w.gpio_out_data_w1tc().variant(1 << PIN)); + } + + #[cfg(esp32s3)] + if level { + rtc_io + .rtc_gpio_out_w1ts + .write(|w| w.rtc_gpio_out_data_w1ts().variant(1 << PIN)); + } else { + rtc_io + .rtc_gpio_out_w1tc + .write(|w| w.rtc_gpio_out_data_w1tc().variant(1 << PIN)); + } + } + + #[doc(hidden)] + pub fn get_level(&self) -> bool { + let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR }; + (rtc_io.rtc_gpio_in.read().bits() & 1 << PIN) != 0 + } + + /// Configures the pin as an input with the internal pull-up resistor + /// enabled. + pub fn into_pull_up_input(self) -> LowPowerPin, PIN> { + self.input_enable(true); + self.pullup_enable(true); + self.pulldown_enable(false); + LowPowerPin { + private: PhantomData::default(), + } + } + + /// Configures the pin as an input with the internal pull-down resistor + /// enabled. + pub fn into_pull_down_input(self) -> LowPowerPin, PIN> { + self.input_enable(true); + self.pullup_enable(false); + self.pulldown_enable(true); + LowPowerPin { + private: PhantomData::default(), + } + } + + /// Configures the pin as a floating input pin. + pub fn into_floating_input(self) -> LowPowerPin, PIN> { + self.input_enable(true); + self.pullup_enable(false); + self.pulldown_enable(false); + LowPowerPin { + private: PhantomData::default(), + } + } + + /// Configures the pin as an output pin. + pub fn into_push_pull_output(self) -> LowPowerPin, PIN> { + self.output_enable(true); + LowPowerPin { + private: PhantomData::default(), + } + } + } + + #[cfg(esp32s3)] + #[inline(always)] + fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD0 { + let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR }; + unsafe { core::mem::transmute((rtc_io.touch_pad0.as_ptr()).add(pin as usize)) } + } + + #[cfg(esp32s2)] + #[inline(always)] + fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD { + let rtc_io = unsafe { &*crate::peripherals::RTC_IO::PTR }; + unsafe { core::mem::transmute((rtc_io.touch_pad[0].as_ptr()).add(pin as usize)) } + } +} + #[cfg(lp_io)] pub mod lp_gpio { //! Low Power IO (LP_IO) diff --git a/esp-hal-procmacros/Cargo.toml b/esp-hal-procmacros/Cargo.toml index 96be06c9b..3bd4f3f55 100644 --- a/esp-hal-procmacros/Cargo.toml +++ b/esp-hal-procmacros/Cargo.toml @@ -35,8 +35,8 @@ esp32c2 = [] esp32c3 = [] esp32c6 = ["dep:object"] esp32h2 = [] -esp32s2 = [] -esp32s3 = [] +esp32s2 = ["dep:object"] +esp32s3 = ["dep:object"] interrupt = [] rtc_slow = [] diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs index 6ad2f7052..dd7cecdd6 100644 --- a/esp-hal-procmacros/src/lib.rs +++ b/esp-hal-procmacros/src/lib.rs @@ -514,7 +514,7 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream { .into() } -#[cfg(feature = "esp32c6")] +#[cfg(any(feature = "esp32c6", feature = "esp32s2", feature = "esp32s3"))] #[proc_macro] pub fn load_lp_code(input: TokenStream) -> TokenStream { use object::{Object, ObjectSection, ObjectSymbol}; @@ -524,9 +524,17 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { #[cfg(feature = "esp32c6")] let hal_crate = crate_name("esp32c6-hal"); + #[cfg(feature = "esp32s2")] + let hal_crate = crate_name("esp32s2-hal"); + #[cfg(feature = "esp32s3")] + let hal_crate = crate_name("esp32s3-hal"); #[cfg(feature = "esp32c6")] let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into()); + #[cfg(feature = "esp32s2")] + let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into()); + #[cfg(feature = "esp32s3")] + let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into()); let hal_crate = match hal_crate { Ok(FoundCrate::Itself) => { @@ -577,12 +585,21 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { let mut sections: Vec = sections .into_iter() - .filter(|section| section.address() != 0) + .filter(|section| match section.kind() { + object::SectionKind::Text + | object::SectionKind::ReadOnlyData + | object::SectionKind::Data + | object::SectionKind::UninitializedData => true, + _ => false, + }) .collect(); sections.sort_by(|a, b| a.address().partial_cmp(&b.address()).unwrap()); let mut binary: Vec = Vec::new(); + #[cfg(feature = "esp32c6")] let mut last_address = 0x50_000_000; + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + let mut last_address = 0x0; for section in sections { if section.address() > last_address { @@ -602,7 +619,7 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { if let None = magic_symbol { return parse::Error::new( Span::call_site().into(), - "Given file doesn't seem to be an LP core application.", + "Given file doesn't seem to be an LP/ULP core application.", ) .to_compile_error() .into(); @@ -621,12 +638,28 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { .filter(|v: &proc_macro2::TokenStream| !v.is_empty()) .collect(); + #[cfg(feature = "esp32c6")] + let imports = quote! { + use #hal_crate::lp_core::LpCore; + use #hal_crate::lp_core::LpCoreWakeupSource; + use #hal_crate::gpio::lp_gpio::LowPowerPin; + use #hal_crate::gpio::*; + }; + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + let imports = quote! { + use #hal_crate::ulp_core::UlpCore as LpCore; + use #hal_crate::ulp_core::UlpCoreWakeupSource as LpCoreWakeupSource; + use #hal_crate::gpio::*; + }; + + #[cfg(feature = "esp32c6")] + let rtc_code_start = quote! { _rtc_fast_data_start }; + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + let rtc_code_start = quote! { _rtc_slow_data_start }; + quote! { { - use #hal_crate::lp_core::LpCore; - use #hal_crate::lp_core::LpCoreWakeupSource; - use #hal_crate::gpio::lp_gpio::LowPowerPin; - use #hal_crate::gpio::*; + #imports struct LpCoreCode { } @@ -634,11 +667,11 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { static LP_CODE: &[u8] = &[#(#binary),*]; extern "C" { - static _rtc_fast_data_start: u32; + static #rtc_code_start: u32; } unsafe { - core::ptr::copy_nonoverlapping(LP_CODE as *const _ as *const u8, &_rtc_fast_data_start as *const u32 as *mut u8, LP_CODE.len()); + core::ptr::copy_nonoverlapping(LP_CODE as *const _ as *const u8, &#rtc_code_start as *const u32 as *mut u8, LP_CODE.len()); } impl LpCoreCode { diff --git a/esp32c6-lp-hal-procmacros/Cargo.toml b/esp32c6-lp-hal-procmacros/Cargo.toml deleted file mode 100644 index 6fe22168f..000000000 --- a/esp32c6-lp-hal-procmacros/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "esp32c6-lp-hal-procmacros" -version = "0.1.0" -edition = "2021" -rust-version = "1.67.0" -description = "Procedural macros for the ESP32-C6's LP coprocessor" -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", -] - -[lib] -proc-macro = true - -[dependencies] -litrs = "0.4.0" -proc-macro-crate = "1.3.1" -proc-macro-error = "1.0.4" -proc-macro2 = "1.0.63" -quote = "1.0.28" -syn = {version = "2.0.22", features = ["extra-traits", "full"]} -toml_edit = "0.20.0" diff --git a/esp32c6-lp-hal/Cargo.toml b/esp32c6-lp-hal/Cargo.toml index 8079a6ba2..795d3c956 100644 --- a/esp32c6-lp-hal/Cargo.toml +++ b/esp32c6-lp-hal/Cargo.toml @@ -24,7 +24,7 @@ categories = [ critical-section = { version = "1.1.2", features = ["restore-state-u8"] } embedded-hal = { version = "0.2.7", features = ["unproven"] } esp32c6-lp = { git = "https://github.com/esp-rs/esp-pacs", rev = "a9cad5e", features = ["critical-section"] } -esp32c6-lp-hal-procmacros = { path = "../esp32c6-lp-hal-procmacros" } +lp-hal-procmacros = { path = "../lp-hal-procmacros", features = ["esp32c6"] } riscv = "0.10.1" paste = "1.0.14" diff --git a/esp32c6-lp-hal/src/prelude.rs b/esp32c6-lp-hal/src/prelude.rs index d52b9c7e7..41f9afdb8 100644 --- a/esp32c6-lp-hal/src/prelude.rs +++ b/esp32c6-lp-hal/src/prelude.rs @@ -12,4 +12,4 @@ pub use embedded_hal::{ }, prelude::*, }; -pub use esp32c6_lp_hal_procmacros::entry; +pub use lp_hal_procmacros::entry; diff --git a/esp32s2-hal/examples/ulp_riscv_core_basic.rs b/esp32s2-hal/examples/ulp_riscv_core_basic.rs index 8e97dce25..d4d8448e9 100644 --- a/esp32s2-hal/examples/ulp_riscv_core_basic.rs +++ b/esp32s2-hal/examples/ulp_riscv_core_basic.rs @@ -1,32 +1,21 @@ //! This shows a very basic example of running code on the ULP RISCV core. //! -//! Code on ULP core just increments a counter. The current value is printed by -//! the HP core. +//! Code on ULP core just increments a counter and blinks GPIO 1. The current +//! value is printed by the HP core. #![no_std] #![no_main] -use esp32s2_hal::{clock::ClockControl, peripherals::Peripherals, prelude::*}; +use esp32s2_hal::{ + clock::ClockControl, + gpio::rtc_io::*, + peripherals::Peripherals, + prelude::*, + ulp_core, + IO, +}; use esp_backtrace as _; -use esp_println::println; - -// 50000000 <_start>: -// 50000000: 00000517 auipc a0,0x0 -// 50000004: 01050513 addi a0,a0,16 # 50000010 -// 50000008: 4581 li a1,0 -// -// 5000000a <_loop>: -// 5000000a: 0585 addi a1,a1,1 -// 5000000c: c10c sw a1,0(a0) -// 5000000e: bff5 j 5000000a <_loop> -// -// 50000010 : -// 50000010: 0000 0000 - -const CODE: &[u8] = &[ - 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x01, 0x81, 0x45, 0x85, 0x05, 0x0c, 0xc1, 0xf5, 0xbf, - 0x00, 0x00, 0x00, 0x00, -]; +use esp_println::{print, println}; #[entry] fn main() -> ! { @@ -34,21 +23,24 @@ fn main() -> ! { let system = peripherals.SYSTEM.split(); let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let mut ulp_core = esp32s2_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let pin = io.pins.gpio1.into_low_power().into_push_pull_output(); - // copy code to RTC ram - let lp_ram = 0x5000_0000 as *mut u8; - unsafe { - core::ptr::copy_nonoverlapping(CODE as *const _ as *const u8, lp_ram, CODE.len()); - } - println!("copied code (len {})", CODE.len()); + let mut ulp_core = ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); - // start ULP core - ulp_core.run(esp32s2_hal::ulp_core::UlpCoreWakeupSource::HpCpu); + // load code to LP core + let lp_core_code = load_lp_code!( + "../ulp-riscv-hal/target/riscv32imc-unknown-none-elf/release/examples/blinky" + ); + + // start LP core + lp_core_code.run(&mut ulp_core, ulp_core::UlpCoreWakeupSource::HpCpu, pin); println!("ulpcore run"); - let data = (0x5000_0010 - 0) as *mut u32; + let data = (0x5000_0400) as *mut u32; loop { - println!("Current {}", unsafe { data.read_volatile() }); + print!("Current {:x} \u{000d}", unsafe { + data.read_volatile() + }); } } diff --git a/esp32s3-hal/examples/ulp_riscv_core_basic.rs b/esp32s3-hal/examples/ulp_riscv_core_basic.rs index b0f8b9666..701a5eeab 100644 --- a/esp32s3-hal/examples/ulp_riscv_core_basic.rs +++ b/esp32s3-hal/examples/ulp_riscv_core_basic.rs @@ -1,32 +1,21 @@ //! This shows a very basic example of running code on the ULP RISCV core. //! -//! Code on ULP core just increments a counter. The current value is printed by -//! the HP core. +//! Code on ULP core just increments a counter and blinks GPIO 1. The current +//! value is printed by the HP core. #![no_std] #![no_main] -use esp32s3_hal::{clock::ClockControl, peripherals::Peripherals, prelude::*}; +use esp32s3_hal::{ + clock::ClockControl, + gpio::rtc_io::*, + peripherals::Peripherals, + prelude::*, + ulp_core, + IO, +}; use esp_backtrace as _; -use esp_println::println; - -// 50000000 <_start>: -// 50000000: 00000517 auipc a0,0x0 -// 50000004: 01050513 addi a0,a0,16 # 50000010 -// 50000008: 4581 li a1,0 -// -// 5000000a <_loop>: -// 5000000a: 0585 addi a1,a1,1 -// 5000000c: c10c sw a1,0(a0) -// 5000000e: bff5 j 5000000a <_loop> -// -// 50000010 : -// 50000010: 0000 0000 - -const CODE: &[u8] = &[ - 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x01, 0x81, 0x45, 0x85, 0x05, 0x0c, 0xc1, 0xf5, 0xbf, - 0x00, 0x00, 0x00, 0x00, -]; +use esp_println::{print, println}; #[entry] fn main() -> ! { @@ -34,23 +23,26 @@ fn main() -> ! { let system = peripherals.SYSTEM.split(); let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let mut ulp_core = esp32s3_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let pin = io.pins.gpio1.into_low_power().into_push_pull_output(); + + let mut ulp_core = ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); ulp_core.stop(); println!("ulp core stopped"); - // copy code to RTC ram - let lp_ram = 0x5000_0000 as *mut u8; - unsafe { - core::ptr::copy_nonoverlapping(CODE as *const _ as *const u8, lp_ram, CODE.len()); - } - println!("copied code (len {})", CODE.len()); + // load code to LP core + let lp_core_code = load_lp_code!( + "../ulp-riscv-hal/target/riscv32imc-unknown-none-elf/release/examples/blinky" + ); - // start ULP core - ulp_core.run(esp32s3_hal::ulp_core::UlpCoreWakeupSource::HpCpu); + // start LP core + lp_core_code.run(&mut ulp_core, ulp_core::UlpCoreWakeupSource::HpCpu, pin); println!("ulpcore run"); - let data = (0x5000_0010 - 0) as *mut u32; + let data = (0x5000_0400) as *mut u32; loop { - println!("Current {}", unsafe { data.read_volatile() }); + print!("Current {:x} \u{000d}", unsafe { + data.read_volatile() + }); } } diff --git a/lp-hal-procmacros/Cargo.toml b/lp-hal-procmacros/Cargo.toml new file mode 100644 index 000000000..87569effa --- /dev/null +++ b/lp-hal-procmacros/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "lp-hal-procmacros" +version = "0.1.0" +edition = "2021" +rust-version = "1.67.0" +description = "Procedural macros for the LP/ULP coprocessors" +repository = "https://github.com/esp-rs/esp-hal" +license = "MIT OR Apache-2.0" + +keywords = [ + "embedded", + "embedded-hal", + "esp", + "esp32c6", + "esp32s2", + "esp32s3", + "no-std", +] +categories = ["embedded", "hardware-support", "no-std"] + +[lib] +proc-macro = true + +[dependencies] +litrs = "0.4.0" +proc-macro-crate = "1.3.1" +proc-macro-error = "1.0.4" +proc-macro2 = "1.0.63" +quote = "1.0.28" +syn = { version = "2.0.22", features = ["extra-traits", "full"] } +toml_edit = "0.20.0" + +[features] +esp32c6 = [] +esp32s2 = [] +esp32s3 = [] diff --git a/esp32c6-lp-hal-procmacros/src/lib.rs b/lp-hal-procmacros/src/lib.rs similarity index 93% rename from esp32c6-lp-hal-procmacros/src/lib.rs rename to lp-hal-procmacros/src/lib.rs index 965ba1fa4..5b7dcca3f 100644 --- a/esp32c6-lp-hal-procmacros/src/lib.rs +++ b/lp-hal-procmacros/src/lib.rs @@ -8,11 +8,17 @@ use syn::{parse, parse_macro_input, spanned::Spanned, FnArg, ItemFn, PatType}; #[proc_macro_error] #[proc_macro_attribute] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + #[cfg(feature = "esp32c6")] let found_crate = crate_name("esp32c6-lp-hal").expect("esp32c6_lp_hal is present in `Cargo.toml`"); + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + let found_crate = crate_name("ulp-riscv-hal").expect("ulp-riscv-ha is present in `Cargo.toml`"); let hal_crate = match found_crate { + #[cfg(feature = "esp32c6")] FoundCrate::Itself => quote!(esp32c6_lp_hal), + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + FoundCrate::Itself => quote!(ulp_riscv_hal), FoundCrate::Name(name) => { let ident = Ident::new(&name, Span::call_site()); quote!( #ident::Something ) diff --git a/ulp-riscv-hal/.cargo/config.toml b/ulp-riscv-hal/.cargo/config.toml new file mode 100644 index 000000000..3a0e33d7d --- /dev/null +++ b/ulp-riscv-hal/.cargo/config.toml @@ -0,0 +1,11 @@ +[target.riscv32imc-unknown-none-elf] +runner = "espflash flash --monitor" +rustflags = [ + "-C", "link-arg=-Tlink.x", +] + +[build] +target = "riscv32imc-unknown-none-elf" + +[unstable] +build-std = ["core"] diff --git a/ulp-riscv-hal/CHANGELOG.md b/ulp-riscv-hal/CHANGELOG.md new file mode 100644 index 000000000..a983d12f5 --- /dev/null +++ b/ulp-riscv-hal/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Add the `esp32c6-lp-hal` package (#714) +- Add GPIO (output) and delay functionality to `esp32c6-lp-hal` (#715) +- Add GPIO input support and implement additional `embedded-hal` output traits for the C6's LP core [#720] + +### Changed + +### Fixed + +### Removed + +[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp32c6-lp-hal diff --git a/ulp-riscv-hal/Cargo.toml b/ulp-riscv-hal/Cargo.toml new file mode 100644 index 000000000..d0886c433 --- /dev/null +++ b/ulp-riscv-hal/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "ulp-riscv-hal" +version = "0.1.0" +edition = "2021" +rust-version = "1.67.0" +description = "HAL for ESP32-S2 / ESP32-S3 ULP RISC-V coprocessor" +repository = "https://github.com/esp-rs/esp-hal" +license = "MIT OR Apache-2.0" + +keywords = [ + "embedded", + "embedded-hal", + "esp", + "esp32s2", + "esp32s3", + "no-std", +] +categories = [ + "embedded", + "hardware-support", + "no-std", +] + +[dependencies] +embedded-hal = { version = "0.2.7", features = ["unproven"] } +lp-hal-procmacros = { path = "../lp-hal-procmacros" } +paste = "1.0.14" +esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s2-ulp", optional = true } +esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s3-ulp", optional = true } + + +[dev-dependencies] +panic-halt = "0.2.0" + +[features] +default = [] +debug = [] +esp32s2 = [ "dep:esp32s2-ulp", "lp-hal-procmacros/esp32s2" ] +esp32s3 = [ "dep:esp32s3-ulp", "lp-hal-procmacros/esp32s3" ] diff --git a/ulp-riscv-hal/README.md b/ulp-riscv-hal/README.md new file mode 100644 index 000000000..3dac4619a --- /dev/null +++ b/ulp-riscv-hal/README.md @@ -0,0 +1,53 @@ +# ulp-lp-hal + +[![Crates.io](https://img.shields.io/crates/v/ulp-lp-hal?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/ulp-lp-hal) +[![docs.rs](https://img.shields.io/docsrs/ulp-lp-hal?labelColor=1C2C2E&color=C96329&logo=rust&style=flat-square)](https://docs.rs/ulp-lp-hal) +![Crates.io](https://img.shields.io/crates/l/ulp-lp-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-S2/ESP32-S3 from Espressif's ultra-low-power coprocessor. + +Implements a number of the traits defined in [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 `riscv32imc-unknown-none-elf` target. + +Please refer to the documentation for more information. + +## [Documentation] + +[documentation]: https://docs.rs/ulp-lp-hal/ + +## Resources + +- [Datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf) +- [Datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf) +- [Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf) +- [Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf) +- [The Rust Programming Language](https://doc.rust-lang.org/book/) +- [The Embedded Rust Book](https://docs.rust-embedded.org/book/index.html) +- [The Rust on ESP Book](https://esp-rs.github.io/book/) + +## Getting Started + +### Installing the Rust Compiler Target + +The compilation target for this device is officially supported by the mainline Rust compiler and can be installed using [rustup](https://rustup.rs/): + +```shell +rustup target add riscv32imc-unknown-none-elf +``` + +## 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/ulp-riscv-hal/build.rs b/ulp-riscv-hal/build.rs new file mode 100644 index 000000000..f6e11c0bc --- /dev/null +++ b/ulp-riscv-hal/build.rs @@ -0,0 +1,17 @@ +use std::{env, fs::File, io::Write, path::PathBuf}; + +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("link.x")) + .unwrap() + .write_all(include_bytes!("ld/link.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"); +} diff --git a/ulp-riscv-hal/examples/blinky.rs b/ulp-riscv-hal/examples/blinky.rs new file mode 100644 index 000000000..a72d977e2 --- /dev/null +++ b/ulp-riscv-hal/examples/blinky.rs @@ -0,0 +1,35 @@ +//! Counts a 32 bit value at 0x5000_0400 (0x400 from the ULP's point of view) +//! and blinks GPIO 1. +//! +//! Make sure the RTC RAM is cleared before loading the code. + +#![no_std] +#![no_main] + +use panic_halt as _; +use ulp_riscv_hal::{ + delay::Delay, + gpio::{GpioPin, Output, PushPull}, + prelude::*, +}; + +#[entry] +fn main(mut gpio1: GpioPin, 1>) -> ! { + let mut i: u32 = 0; + + let ptr = 0x400 as *mut u32; + let mut delay = Delay::new(); + + loop { + i = i.wrapping_add(1u32); + unsafe { + ptr.write_volatile(i); + } + + gpio1.set_high().unwrap(); + delay.delay_ms(500); + + gpio1.set_low().unwrap(); + delay.delay_ms(500); + } +} diff --git a/ulp-riscv-hal/ld/link.x b/ulp-riscv-hal/ld/link.x new file mode 100644 index 000000000..89db971d7 --- /dev/null +++ b/ulp-riscv-hal/ld/link.x @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "sdkconfig.h" + +ENTRY(reset_vector) + +CONFIG_ULP_COPROC_RESERVE_MEM = 8 * 1024; + +MEMORY +{ + ram(RW) : ORIGIN = 0, LENGTH = CONFIG_ULP_COPROC_RESERVE_MEM +} + +SECTIONS +{ + . = ORIGIN(ram); + .text : + { + *(.text.vectors) /* Default reset vector must link to offset 0x0 */ + + KEEP(*(.init)); + KEEP(*(.init.rust)); + *(.text) + *(.text*) + } >ram + + .rodata ALIGN(4): + { + *(.rodata) + *(.rodata*) + } > ram + + .data ALIGN(4): + { + *(.data) + *(.data*) + *(.sdata) + *(.sdata*) + } > ram + + .bss ALIGN(4) : + { + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + } >ram + + __stack_top = ORIGIN(ram) + LENGTH(ram); +} diff --git a/ulp-riscv-hal/src/delay.rs b/ulp-riscv-hal/src/delay.rs new file mode 100644 index 000000000..3e55b701a --- /dev/null +++ b/ulp-riscv-hal/src/delay.rs @@ -0,0 +1,62 @@ +//! Simple blocking delay functionality +//! +//! This uses cycle count under the hood. + +use core::arch::asm; + +use embedded_hal::blocking::delay::{DelayMs, DelayUs}; + +// see components\ulp\ulp_riscv\ulp_core\include\ulp_riscv_utils.h in esp-idf +#[cfg(feature = "esp32s2")] +const CYCLES_PER_US_M10: u32 = 85; +#[cfg(feature = "esp32s3")] +const CYCLES_PER_US_M10: u32 = 175; + +pub struct Delay {} + +impl Delay { + pub fn new() -> Self { + Self {} + } +} + +impl Default for Delay { + fn default() -> Self { + Self::new() + } +} + +impl DelayUs for Delay { + #[inline(always)] + fn delay_us(&mut self, us: u64) { + DelayUs::::delay_us(self, us as u32); + } +} + +impl DelayUs for Delay { + #[inline(always)] + fn delay_us(&mut self, us: u32) { + let t0 = cycles(); + let clock = us * CYCLES_PER_US_M10 / 10; + while cycles().wrapping_sub(t0) <= clock {} + } +} + +impl DelayMs for Delay { + #[inline(always)] + fn delay_ms(&mut self, ms: u32) { + DelayUs::::delay_us(self, ms * 1000); + } +} + +#[inline(always)] +fn cycles() -> u32 { + let mut cycles; + unsafe { + asm!( + "rdcycle {cycles}", + cycles = out(reg) cycles, + ) + } + cycles +} diff --git a/ulp-riscv-hal/src/gpio.rs b/ulp-riscv-hal/src/gpio.rs new file mode 100644 index 000000000..e8c952d74 --- /dev/null +++ b/ulp-riscv-hal/src/gpio.rs @@ -0,0 +1,122 @@ +//! Low-power GPIO driver +//! +//! It's assumed that GPIOs are already configured correctly by the HP core. + +use core::{convert::Infallible, marker::PhantomData}; + +use crate::pac::RTC_IO; + +#[doc(hidden)] +pub unsafe fn conjour() -> Option> { + if PIN > 7 { + None + } else { + Some(GpioPin { + phantom: PhantomData, + }) + } +} + +pub struct Unknown {} + +pub struct Input { + _mode: PhantomData, +} + +pub struct Floating; + +pub struct PullDown; + +pub struct PullUp; + +pub struct Output { + _mode: PhantomData, +} + +pub struct PushPull; + +#[non_exhaustive] +pub struct GpioPin { + phantom: PhantomData, +} + +impl GpioPin, PIN> { + fn input_state(&self) -> bool { + unsafe { &*RTC_IO::PTR }.rtc_gpio_in.read().bits() >> PIN & 0x1 != 0 + } +} + +impl GpioPin, PIN> { + fn output_state(&self) -> bool { + unsafe { &*RTC_IO::PTR }.rtc_gpio_out.read().bits() >> PIN & 0x1 != 0 + } + + fn set_output_low(&mut self) { + // TODO align PAC + + #[cfg(feature = "esp32s2")] + unsafe { &*RTC_IO::PTR } + .rtc_gpio_out_w1tc + .write(|w| w.gpio_out_data_w1tc().variant(1 << PIN)); + + #[cfg(feature = "esp32s3")] + unsafe { &*RTC_IO::PTR } + .rtc_gpio_out_w1tc + .write(|w| w.rtc_gpio_out_data_w1tc().variant(1 << PIN)); + } + + fn set_output_high(&mut self) { + #[cfg(feature = "esp32s2")] + unsafe { &*RTC_IO::PTR } + .rtc_gpio_out_w1ts + .write(|w| w.gpio_out_data_w1ts().variant(1 << PIN)); + + #[cfg(feature = "esp32s3")] + unsafe { &*RTC_IO::PTR } + .rtc_gpio_out_w1ts + .write(|w| w.rtc_gpio_out_data_w1ts().variant(1 << PIN)); + } +} + +impl embedded_hal::digital::v2::InputPin for GpioPin, PIN> { + type Error = Infallible; + + fn is_high(&self) -> Result { + Ok(self.input_state()) + } + + fn is_low(&self) -> Result { + Ok(!self.is_high()?) + } +} + +impl embedded_hal::digital::v2::OutputPin for GpioPin, PIN> { + type Error = Infallible; + + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_output_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_output_high(); + Ok(()) + } +} + +impl embedded_hal::digital::v2::StatefulOutputPin + for GpioPin, PIN> +{ + fn is_set_high(&self) -> Result { + Ok(self.output_state()) + } + + fn is_set_low(&self) -> Result { + Ok(!self.is_set_high()?) + } +} + +impl embedded_hal::digital::v2::toggleable::Default + for GpioPin, PIN> +{ +} diff --git a/ulp-riscv-hal/src/lib.rs b/ulp-riscv-hal/src/lib.rs new file mode 100644 index 000000000..e98f25228 --- /dev/null +++ b/ulp-riscv-hal/src/lib.rs @@ -0,0 +1,97 @@ +#![no_std] + +use core::arch::global_asm; + +pub mod delay; +pub mod gpio; +pub mod prelude; + +#[cfg(feature = "esp32s2")] +use esp32s2_ulp as pac; +#[cfg(feature = "esp32s3")] +use esp32s3_ulp as pac; + +global_asm!( + r#" + .section .text.vectors + .global irq_vector + .global reset_vector + +/* The reset vector, jumps to startup code */ +reset_vector: + j __start + +/* Interrupt handler */ +.balign 16 +irq_vector: + ret + + .section .text + +__start: + /* setup the stack pointer */ + la sp, __stack_top + + call ulp_riscv_rescue_from_monitor + call rust_main + call ulp_riscv_halt +loop: + j loop +"# +); + +#[link_section = ".init.rust"] +#[export_name = "rust_main"] +unsafe extern "C" fn lp_core_startup() -> ! { + extern "Rust" { + fn main() -> !; + } + + main(); +} + +#[link_section = ".init.rust"] +#[export_name = "ulp_riscv_rescue_from_monitor"] +unsafe extern "C" fn ulp_riscv_rescue_from_monitor() { + // Rescue RISCV from monitor state. + let rtc_cntl = unsafe { pac::RTC_CNTL::steal() }; + + // TODO align naming in PACs + #[cfg(feature = "esp32s2")] + rtc_cntl + .cocpu_ctrl + .modify(|_, w| w.cocpu_done().clear_bit().cocpu_shut_reset_en().clear_bit()); + #[cfg(feature = "esp32s3")] + rtc_cntl + .rtc_cocpu_ctrl + .modify(|_, w| w.cocpu_done().clear_bit().cocpu_shut_reset_en().clear_bit()); +} + +#[link_section = ".init.rust"] +#[export_name = "ulp_riscv_halt"] +unsafe extern "C" fn ulp_riscv_halt() { + let rtc_cntl = unsafe { pac::RTC_CNTL::steal() }; + + // TODO align naming in PACs + #[cfg(feature = "esp32s2")] + { + rtc_cntl + .cocpu_ctrl + .modify(|_, w| w.cocpu_shut_2_clk_dis().variant(0x3f)); + + rtc_cntl.cocpu_ctrl.modify(|_, w| w.cocpu_done().set_bit()); + } + #[cfg(feature = "esp32s3")] + { + rtc_cntl + .rtc_cocpu_ctrl + .modify(|_, w| w.cocpu_shut_2_clk_dis().variant(0x3f)); + + rtc_cntl + .rtc_cocpu_ctrl + .modify(|_, w| w.cocpu_done().set_bit()); + } + + #[allow(clippy::empty_loop)] + loop {} +} diff --git a/ulp-riscv-hal/src/prelude.rs b/ulp-riscv-hal/src/prelude.rs new file mode 100644 index 000000000..41f9afdb8 --- /dev/null +++ b/ulp-riscv-hal/src/prelude.rs @@ -0,0 +1,15 @@ +//! The prelude +//! +//! Re-exports all traits required for interacting with the various peripheral +//! drivers implemented in this crate. + +pub use embedded_hal::{ + digital::v2::{ + InputPin as _embedded_hal_digital_vs_InputPin, + OutputPin as _embedded_hal_digital_vs_OutputPin, + StatefulOutputPin as _embedded_hal_digital_vs_StatefulOutputPin, + ToggleableOutputPin as _embedded_hal_digital_vs_ToggleableOutputPin, + }, + prelude::*, +}; +pub use lp_hal_procmacros::entry;