Skip to content
Draft
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
3daab42
Add IRQ handler, add custom R-type instruction macros, move out reset…
leighleighleigh Mar 17, 2026
5e2846e
Moving some riscv-rt stuff into here
leighleighleigh Mar 20, 2026
7f4bdd7
ULP is working, no interrupt handling yet
leighleighleigh Mar 21, 2026
3017228
Updated interrupt handler to save/restore TrapFrame like riscv-rt, bu…
leighleighleigh Mar 21, 2026
0503f7f
Really basic interrupt handling is working, adapted from picorv32-rt
leighleighleigh Mar 21, 2026
ef03250
Undo some non-functional formatting changes to reduce churn
leighleighleigh Mar 22, 2026
8f9e92f
Added some gross interrupt binding macros, re-used the SENS interrupt…
leighleighleigh Mar 23, 2026
6b4f2b5
Hacky .listen() and .unlisten() methods on GPIO pins
leighleighleigh Mar 23, 2026
6cccbf1
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Mar 23, 2026
ccfc8d8
Add GPIO wakeup method to LP gpio pins.
leighleighleigh Mar 23, 2026
8bd4cb6
Hide interrupt macros on non-supported chips
leighleighleigh Mar 23, 2026
f50d455
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Mar 27, 2026
8d0749e
Exploring a cleaner interrupt binding approach
leighleighleigh Mar 28, 2026
d344dfc
Satisfy clippy
leighleighleigh Mar 28, 2026
2c1a07e
Incomplete migration of a bunch of interrupt handling stuffs
leighleighleigh Mar 29, 2026
6df37f5
Semi-functional port of Io interrupt handling APIs
leighleighleigh Mar 29, 2026
f03e89b
Cleanup feature gating, add non-working critical section impl
leighleighleigh Mar 30, 2026
2a6ed84
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Apr 2, 2026
fce1661
Updated critical section implementation to handle nesting properly
leighleighleigh Apr 2, 2026
250fe93
WIP Switch to using interrupt vectors exported by PAC
leighleighleigh Apr 2, 2026
2c31ed0
Update interrupt_counter.rs to align with ESP-HAL example
leighleighleigh Apr 3, 2026
f714b6f
Added interupt::disable() function
leighleighleigh Apr 3, 2026
d35fcbd
Make clippy happy again
leighleighleigh Apr 3, 2026
e081c2f
Remove duplicate example name causing havok in CI
leighleighleigh Apr 3, 2026
20e8d0e
Update CHANGELOG.md
leighleighleigh Apr 3, 2026
4e63d55
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Apr 3, 2026
abdbca3
Update link-ulp.x
leighleighleigh Apr 3, 2026
407ca83
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Apr 7, 2026
6c9d54d
Clear `DEVICE_PERIPHERALS` PAC variable to prevent `Peripheral::take(…
leighleighleigh Apr 7, 2026
fecb21f
Dont overwrite code region in lp_core::load_lp_code, until run is called
leighleighleigh Apr 7, 2026
d2343bf
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Apr 11, 2026
899f0a2
Update interrupt_counter.rs
leighleighleigh Apr 12, 2026
d8c3623
Address comments, revert procmacro change to avoid breaking user code…
leighleighleigh Apr 12, 2026
d203bae
Added HP core example
leighleighleigh Apr 12, 2026
fc52c21
Update changelog
leighleighleigh Apr 12, 2026
83fbf87
Update HP core example
leighleighleigh Apr 12, 2026
c87caba
Add ULP-core APIs for GPIO wakeup
leighleighleigh Apr 12, 2026
30be234
Bump PAC version, add GPIO wakeup_enable to rtc_io pins, update examples
leighleighleigh Apr 13, 2026
c7b203c
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Apr 13, 2026
12841ea
Update gpio_wakeup.rs
leighleighleigh Apr 13, 2026
5672c72
Update interrupt_counter.rs
leighleighleigh Apr 13, 2026
f5e008f
Remove feature flag requirement on lp examples, use CHIPS to gate the…
leighleighleigh Apr 14, 2026
53d72f1
Added 'interrupts' feature flag for ULP core interrupt support. Added…
leighleighleigh Apr 16, 2026
1e252b2
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh Apr 18, 2026
787b431
Tweak gpio_wakeup_enable, update deps
leighleighleigh Apr 18, 2026
a0b0b3f
Hide interrupt support from ESP32C6 until implemented
leighleighleigh Apr 18, 2026
013b2f2
Fix rtc_io.wakeup_enable on esp32s2
leighleighleigh Apr 18, 2026
ebfd35f
Add more stub implementations for esp32c6, so CI can pass
leighleighleigh Apr 18, 2026
f396396
Fix C6 compilation due to stray todo
leighleighleigh Apr 18, 2026
e7a3515
Clean-up of RTC/LP IO wakeup_enable method. Implementing WakeupFromLp…
leighleighleigh Apr 19, 2026
c0f1524
Start fixing up the ESP32C6-LP interrupt handling
leighleighleigh Apr 25, 2026
6e5fa4d
Added interrupt::clear() function
leighleighleigh Apr 27, 2026
294ba4b
Brought back gpio_wakeup_disable
leighleighleigh Apr 28, 2026
e6f5ef2
Merged main into leighleighleigh/ulp-interrupt-handler
leighleighleigh May 2, 2026
662d24f
Bump esp-pacs to use un-released commits
leighleighleigh May 2, 2026
9b066aa
Remove _start_trap assembly code when `interrupts` feature disabled
leighleighleigh May 3, 2026
7ac400f
Merge branch 'main' into leighleighleigh/ulp-interrupt-handler
leighleighleigh May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions esp-lp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add `wake_hp_core` for ESP32-S2 and ESP32-S3 (#5133)
- Add `OutputOpenDrain` GPIO type (#5131)
- ESP32-S2, ESP32-S3: Added ULP interrupt support (#5206)

### Changed
- Changed ULP entrypoint to allow `main()` to return, and halt, to the ULP Timer can be used (#5134)
Expand All @@ -18,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Fix panic when handling buffers larger than 7 bytes in `LpI2c.write` and `LpI2c.read` (#4694)
- ESP32-S2, ESP32-S3: Aligned `ulp_riscv_halt()` with ESP_IDF by setting the `cocpu_shut_reset_en` bit, squashing a rare bug where halting the chip would hang (#5134)
- ESP32-S2, ESP32-S3: Prevent `Peripherals::take().unwrap()` from panicking on second ULP Timer `main()`-loop, by clearing `DEVICE_PERIPHERALS` variable on start (#5206)

### Removed

Expand Down
20 changes: 13 additions & 7 deletions esp-lp-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ embedded-io-06 = { package = "embedded-io", version = "0.6", optional = true
embedded-io-07 = { package = "embedded-io", version = "0.7", optional = true }
nb = { version = "1.1.0", optional = true }
procmacros = { version = "0.21.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
riscv = { version = "0.15", features = ["critical-section-single-hart"] }
riscv = { version = "0.15" }
portable-atomic = { version = "1", default-features = false }
critical-section = { version = "1.2.0", features = ["restore-state-u32"], optional = true }
esp-metadata-generated = { version = "0.3.0", path = "../esp-metadata-generated" }
esp32c6-lp = { version = "0.3.0", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "fc3e6d4" }
esp32s2-ulp = { version = "0.3.0", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "fc3e6d4" }
esp32s3-ulp = { version = "0.3.0", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "fc3e6d4" }
esp32c6-lp = { version = "0.3.0", features = ["critical-section"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "5ff8e30e" }
esp32s2-ulp = { version = "0.3.0", features = ["critical-section", "rt"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "5ff8e30e" }
esp32s3-ulp = { version = "0.3.0", features = ["critical-section", "rt"], optional = true, git = "https://github.com/esp-rs/esp-pacs", rev = "5ff8e30e" }

[dev-dependencies]
panic-halt = "0.2.0"
Expand All @@ -67,11 +69,11 @@ debug = [

# Chip Support Feature Flags
# Target the ESP32-C6.
esp32c6 = ["dep:esp32c6-lp", "esp-metadata-generated/esp32c6", "procmacros/is-lp-core", "dep:nb"]
esp32c6 = ["dep:esp32c6-lp", "esp-metadata-generated/esp32c6", "procmacros/is-lp-core", "dep:nb", "riscv/critical-section-single-hart"]
# Target the ESP32-S2.
esp32s2 = ["dep:esp32s2-ulp", "esp-metadata-generated/esp32s2", "procmacros/is-ulp-core"]
esp32s2 = ["dep:esp32s2-ulp", "esp-metadata-generated/esp32s2", "procmacros/is-ulp-core", "dep:critical-section" ]
# Target the ESP32-S3.
esp32s3 = ["dep:esp32s3-ulp", "esp-metadata-generated/esp32s3", "procmacros/is-ulp-core"]
esp32s3 = ["dep:esp32s3-ulp", "esp-metadata-generated/esp32s3", "procmacros/is-ulp-core", "dep:critical-section" ]

#! ### Trait Implementation Feature Flags
## Implement the traits defined in the `1.0.0` releases of `embedded-hal` and
Expand All @@ -84,6 +86,10 @@ embedded-io = ["dep:embedded-io-06", "dep:embedded-io-07"]
name = "blinky"
required-features = []

[[example]]
name = "interrupt_counter"
required-features = ["esp32s3"]

[[example]]
name = "i2c"
required-features = ["esp32c6"]
Expand Down
78 changes: 78 additions & 0 deletions esp-lp-hal/examples/interrupt_counter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! ULP interrupt-based counter example.
//! Increments a 32 bit counter value at a known point in memory, whenever the ULP program is run.
//! If GPIO0 is pressed, resets the counter.

//% CHIPS: esp32s3

#![no_std]
#![no_main]

extern crate panic_halt;

use core::cell::RefCell;

use critical_section::Mutex;
use esp_lp_hal::{
gpio::{Event, Input, Io},
interrupt::{self, Interrupt},
pac::Peripherals,
prelude::*,
};

const ADDRESS: usize = 0x1000;

static BUTTON: Mutex<RefCell<Option<Input<0>>>> = Mutex::new(RefCell::new(None));

#[entry]
fn main(mut button: Input<0>) {
let peripherals = Peripherals::take().unwrap();
let mut io = Io::new(peripherals.RTC_IO);
io.set_interrupt_handler(gpio_interrupt_handler);

critical_section::with(|cs| {
button.listen(Event::FallingEdge);
BUTTON.borrow_ref_mut(cs).replace(button);
});

interrupt::bind_handler(Interrupt::RISCV_START_INT, startup_interrupt_handler);
}

#[handler]
fn startup_interrupt_handler() {
// Increment the counter every time RISCV_START_INT is triggered
unsafe {
let counter = ADDRESS as *mut u32;
counter.write_volatile(counter.read_volatile() + 1);
}

// On entry, immediately disable any more start-up interrupts.
// This is needed, because RISCV_START_INT is driven from the ULP Timer,
// which may be called multiple times before main() has finished execution.
interrupt::set_enabled(Interrupt::RISCV_START_INT, false);
}

#[handler]
fn gpio_interrupt_handler() {
// Check if BUTTON has an interrupt pending
if critical_section::with(|cs| {
BUTTON
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.is_interrupt_set()
}) {
// The button was the source of the interrupt, reset the counter to 0.
unsafe {
let counter = ADDRESS as *mut u32;
counter.write_volatile(0);
}
}

critical_section::with(|cs| {
BUTTON
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.clear_interrupt()
});
}
77 changes: 42 additions & 35 deletions esp-lp-hal/ld/link-ulp.x
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/

/* Provides interrupt vector symbols */
INCLUDE device.x

ENTRY(reset_vector)

CONFIG_ULP_COPROC_RESERVE_MEM = 8 * 1024;
Expand All @@ -15,39 +18,43 @@ MEMORY

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);
. = ORIGIN(ram);

.text :
{
/* Power-on-reset must be placed at address 0x0 */
KEEP(*(.reset));
/* ULP will jump to 0x10 when an interrupt trap occurs */
. = ALIGN(0x10);
KEEP(*(.trap));
KEEP(*(.init));
KEEP(*(.init.rust));
KEEP(*(.trap.rust));
*(.text .text.*)
} >ram

.rodata ALIGN(4):
{
*(.rodata)
*(.rodata*)
} >ram

.data ALIGN(4):
{
PROVIDE(__global_pointer$ = . + 0x800);
*(.data)
*(.data*)
*(.sdata)
*(.sdata*)
} >ram

.bss ALIGN(4):
{
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
} >ram

__stack_top = ORIGIN(ram) + LENGTH(ram);
}
17 changes: 17 additions & 0 deletions esp-lp-hal/src/critical_section.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use critical_section::RawRestoreState;

use crate::interrupt;

struct UlpCriticalSection;
critical_section::set_impl!(UlpCriticalSection);

unsafe impl critical_section::Impl for UlpCriticalSection {
unsafe fn acquire() -> RawRestoreState {
interrupt::disable_cpu_interrupts()
}

unsafe fn release(previous_state: RawRestoreState) {
// Only re-enable interrupts if they were enabled before the critical section.
interrupt::mask_cpu_interrupts(previous_state);
}
}
Loading
Loading