Skip to content

Commit

Permalink
Improve xtensa interrupt latency (#1735)
Browse files Browse the repository at this point in the history
* Avoid U128

* Avoid unnecessary calls

* CHANGELOG.md

* Simplify

Co-authored-by: Jesse Braham <[email protected]>

---------

Co-authored-by: Jesse Braham <[email protected]>
  • Loading branch information
bjoernQ and jessebraham authored Jul 3, 2024
1 parent cd0272e commit 4f9dc96
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 231 deletions.
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved interrupt latency on RISC-V based chips (#1679)
- `esp_wifi::initialize` no longer requires running maximum CPU clock, instead check it runs above 80MHz. (#1688)
- Move DMA descriptors from DMA Channel to each individual peripheral driver. (#1719)
- Improved interrupt latency on Xtensa based chips (#1735)

### Removed
- uart: Removed `configure_pins` methods (#1592)
Expand Down
4 changes: 4 additions & 0 deletions esp-hal/ld/esp32/esp32.x
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ PROVIDE(__zero_bss = default_mem_hook);
PROVIDE(__init_data = default_mem_hook);
PROVIDE(__post_init = default_post_init);

PROVIDE(__level_1_interrupt = handle_interrupts);
PROVIDE(__level_2_interrupt = handle_interrupts);
PROVIDE(__level_3_interrupt = handle_interrupts);

INCLUDE exception.x

/* ESP32 fixups */
Expand Down
4 changes: 4 additions & 0 deletions esp-hal/ld/esp32s2/esp32s2.x
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ PROVIDE(__zero_bss = default_mem_hook);
PROVIDE(__init_data = default_mem_hook);
PROVIDE(__post_init = default_post_init);

PROVIDE(__level_1_interrupt = handle_interrupts);
PROVIDE(__level_2_interrupt = handle_interrupts);
PROVIDE(__level_3_interrupt = handle_interrupts);

INCLUDE exception.x

/* Fixups for esp32s2 */
Expand Down
4 changes: 4 additions & 0 deletions esp-hal/ld/esp32s3/esp32s3.x
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ PROVIDE(__zero_bss = default_mem_hook);
PROVIDE(__init_data = default_mem_hook);
PROVIDE(__post_init = default_post_init);

PROVIDE(__level_1_interrupt = handle_interrupts);
PROVIDE(__level_2_interrupt = handle_interrupts);
PROVIDE(__level_3_interrupt = handle_interrupts);

INCLUDE exception.x

/* ESP32S3 fixups */
Expand Down
122 changes: 122 additions & 0 deletions esp-hal/src/interrupt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
//! [`RESERVED_INTERRUPTS`].
#![warn(missing_docs)]

use core::ops::BitAnd;

#[cfg(riscv)]
pub use self::riscv::*;
#[cfg(xtensa)]
Expand Down Expand Up @@ -114,3 +116,123 @@ impl InterruptHandler {
(self.f)()
}
}

#[cfg(large_intr_status)]
const STATUS_WORDS: usize = 3;

#[cfg(very_large_intr_status)]
const STATUS_WORDS: usize = 4;

#[cfg(not(any(large_intr_status, very_large_intr_status)))]
const STATUS_WORDS: usize = 2;

/// Representation of peripheral-interrupt status bits.
#[derive(Clone, Copy, Default, Debug)]
pub struct InterruptStatus {
status: [u32; STATUS_WORDS],
}

impl InterruptStatus {
const fn empty() -> Self {
InterruptStatus {
status: [0u32; STATUS_WORDS],
}
}

#[cfg(large_intr_status)]
const fn from(w0: u32, w1: u32, w2: u32) -> Self {
Self {
status: [w0, w1, w2],
}
}

#[cfg(very_large_intr_status)]
const fn from(w0: u32, w1: u32, w2: u32, w3: u32) -> Self {
Self {
status: [w0, w1, w2, w3],
}
}

#[cfg(not(any(large_intr_status, very_large_intr_status)))]
const fn from(w0: u32, w1: u32) -> Self {
Self { status: [w0, w1] }
}

/// Is the given interrupt bit set
pub fn is_set(&self, interrupt: u16) -> bool {
(self.status[interrupt as usize / 32] & (1 << (interrupt as u32 % 32))) != 0
}

/// Set the given interrupt status bit
pub fn set(&mut self, interrupt: u16) {
self.status[interrupt as usize / 32] |= 1 << (interrupt as u32 % 32);
}

/// Return an iterator over the set interrupt status bits
pub fn iterator(&self) -> InterruptStatusIterator {
InterruptStatusIterator {
status: *self,
idx: 0,
}
}
}

impl BitAnd for InterruptStatus {
type Output = InterruptStatus;

fn bitand(self, rhs: Self) -> Self::Output {
#[cfg(large_intr_status)]
return Self::Output {
status: [
self.status[0] & rhs.status[0],
self.status[1] & rhs.status[1],
self.status[2] & rhs.status[2],
],
};

#[cfg(very_large_intr_status)]
return Self::Output {
status: [
self.status[0] & rhs.status[0],
self.status[1] & rhs.status[1],
self.status[2] & rhs.status[2],
self.status[3] & rhs.status[3],
],
};

#[cfg(not(any(large_intr_status, very_large_intr_status)))]
return Self::Output {
status: [
self.status[0] & rhs.status[0],
self.status[1] & rhs.status[1],
],
};
}
}

/// Iterator over set interrupt status bits
pub struct InterruptStatusIterator {
status: InterruptStatus,
idx: usize,
}

impl Iterator for InterruptStatusIterator {
type Item = u8;

fn next(&mut self) -> Option<Self::Item> {
if self.idx == usize::MAX {
return None;
}

for i in self.idx..STATUS_WORDS {
if self.status.status[i] != 0 {
let bit = self.status.status[i].trailing_zeros();
self.idx = i;
self.status.status[i] &= !1 << bit;
return Some((bit + 32 * i as u32) as u8);
}
}
self.idx = usize::MAX;
None
}
}
130 changes: 2 additions & 128 deletions esp-hal/src/interrupt/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
//! interrupt15() => Priority::Priority15
//! ```

use core::ops::BitAnd;

pub use esp_riscv_rt::TrapFrame;
use riscv::register::{mcause, mtvec};

Expand All @@ -24,6 +22,7 @@ pub use self::clic::*;
#[cfg(plic)]
pub use self::plic::*;
pub use self::vectored::*;
use super::InterruptStatus;
use crate::{
peripherals::{self, Interrupt},
Cpu,
Expand Down Expand Up @@ -231,131 +230,6 @@ pub fn disable(_core: Cpu, interrupt: Interrupt) {
}
}

#[cfg(large_intr_status)]
const STATUS_WORDS: usize = 3;

#[cfg(very_large_intr_status)]
const STATUS_WORDS: usize = 4;

#[cfg(not(any(large_intr_status, very_large_intr_status)))]
const STATUS_WORDS: usize = 2;

/// Representation of peripheral-interrupt status bits.
#[derive(Clone, Copy, Default, Debug)]
pub struct InterruptStatus {
status: [u32; STATUS_WORDS],
}

impl InterruptStatus {
const fn empty() -> Self {
InterruptStatus {
#[cfg(large_intr_status)]
status: [0, 0, 0],
#[cfg(very_large_intr_status)]
status: [0, 0, 0, 0],
#[cfg(not(any(large_intr_status, very_large_intr_status)))]
status: [0, 0],
}
}

#[cfg(large_intr_status)]
fn from(w0: u32, w1: u32, w2: u32) -> Self {
Self {
status: [w0, w1, w2],
}
}

#[cfg(very_large_intr_status)]
fn from(w0: u32, w1: u32, w2: u32, w3: u32) -> Self {
Self {
status: [w0, w1, w2, w3],
}
}

#[cfg(not(any(large_intr_status, very_large_intr_status)))]
fn from(w0: u32, w1: u32) -> Self {
Self { status: [w0, w1] }
}

/// Is the given interrupt bit set
pub fn is_set(&self, interrupt: u16) -> bool {
(self.status[interrupt as usize / 32] & (1 << (interrupt as u32 % 32))) != 0
}

/// Set the given interrupt status bit
pub fn set(&mut self, interrupt: u16) {
self.status[interrupt as usize / 32] |= 1 << (interrupt as u32 % 32);
}

/// Return an iterator over the set interrupt status bits
pub fn iterator(&self) -> InterruptStatusIterator {
InterruptStatusIterator {
status: *self,
idx: 0,
}
}
}

impl BitAnd for InterruptStatus {
type Output = InterruptStatus;

fn bitand(self, rhs: Self) -> Self::Output {
#[cfg(large_intr_status)]
return Self::Output {
status: [
self.status[0] & rhs.status[0],
self.status[1] & rhs.status[1],
self.status[2] & rhs.status[2],
],
};

#[cfg(very_large_intr_status)]
return Self::Output {
status: [
self.status[0] & rhs.status[0],
self.status[1] & rhs.status[1],
self.status[2] & rhs.status[2],
self.status[3] & rhs.status[3],
],
};

#[cfg(not(any(large_intr_status, very_large_intr_status)))]
return Self::Output {
status: [
self.status[0] & rhs.status[0],
self.status[1] & rhs.status[1],
],
};
}
}

/// Iterator over set interrupt status bits
pub struct InterruptStatusIterator {
status: InterruptStatus,
idx: usize,
}

impl Iterator for InterruptStatusIterator {
type Item = u8;

fn next(&mut self) -> Option<Self::Item> {
if self.idx == usize::MAX {
return None;
}

for i in self.idx..STATUS_WORDS {
if self.status.status[i] != 0 {
let bit = self.status.status[i].trailing_zeros();
self.idx = i;
self.status.status[i] &= !1 << bit;
return Some((bit + 32 * i as u32) as u8);
}
}
self.idx = usize::MAX;
None
}
}

/// Get status of peripheral interrupts
#[inline]
pub fn get_status(_core: Cpu) -> InterruptStatus {
Expand Down Expand Up @@ -538,7 +412,7 @@ mod vectored {

// this has no effect on level interrupts, but the interrupt may be an edge one
// so we clear it anyway
clear(crate::get_core(), cpu_intr);
clear(core, cpu_intr);

let configured_interrupts = get_configured_interrupts(core, status, unsafe {
core::mem::transmute(INTERRUPT_TO_PRIORITY[cpu_intr as usize - 1] as u8)
Expand Down
Loading

0 comments on commit 4f9dc96

Please sign in to comment.