Skip to content

Commit

Permalink
Add ULP RISC-V HAL (#840)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoernQ authored Oct 10, 2023
1 parent 44e968f commit 47821e6
Show file tree
Hide file tree
Showing 23 changed files with 952 additions and 163 deletions.
156 changes: 105 additions & 51 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
182 changes: 182 additions & 0 deletions esp-hal-common/src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1501,6 +1506,21 @@ macro_rules! rtc_pins {
}
}
}

#[cfg(not(esp32))]
paste::paste!{
impl<MODE> crate::gpio::rtc_io::IntoLowPowerPin<$pin_num> for GpioPin<MODE, $pin_num> {
fn into_low_power(mut self) -> crate::gpio::rtc_io::LowPowerPin<Unknown, $pin_num> {
use crate::gpio::RTCPin;

self.rtc_set_config(false, true, crate::gpio::RtcFunction::Rtc);

crate::gpio::rtc_io::LowPowerPin {
private: core::marker::PhantomData::default(),
}
}
}
}
)?
};

Expand Down Expand Up @@ -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<MODE, const PIN: u8> {
pub(crate) private: PhantomData<MODE>,
}

/// Configures a pin for use as a low power pin
pub trait IntoLowPowerPin<const PIN: u8> {
fn into_low_power(self) -> LowPowerPin<Unknown, { PIN }>;
}

impl<MODE, const PIN: u8> LowPowerPin<MODE, PIN> {
#[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<Input<PullUp>, 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<Input<PullDown>, 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<Input<Floating>, 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<Output<PushPull>, 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)
Expand Down
4 changes: 2 additions & 2 deletions esp-hal-procmacros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ esp32c2 = []
esp32c3 = []
esp32c6 = ["dep:object"]
esp32h2 = []
esp32s2 = []
esp32s3 = []
esp32s2 = ["dep:object"]
esp32s3 = ["dep:object"]

interrupt = []
rtc_slow = []
51 changes: 42 additions & 9 deletions esp-hal-procmacros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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) => {
Expand Down Expand Up @@ -577,12 +585,21 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {

let mut sections: Vec<object::Section> = 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<u8> = 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 {
Expand All @@ -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();
Expand All @@ -621,24 +638,40 @@ 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 {
}

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 {
Expand Down
33 changes: 0 additions & 33 deletions esp32c6-lp-hal-procmacros/Cargo.toml

This file was deleted.

2 changes: 1 addition & 1 deletion esp32c6-lp-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion esp32c6-lp-hal/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ pub use embedded_hal::{
},
prelude::*,
};
pub use esp32c6_lp_hal_procmacros::entry;
pub use lp_hal_procmacros::entry;
Loading

0 comments on commit 47821e6

Please sign in to comment.