perf: vectored interrupt improvements

Instead of building a table of _all_ configured interrupts,
we now only check the currently _pending_ interrupts configuration
status. This means in the best case scenario we only ever do _one_
lookup instead of the 60~ we did before.

Tested on esp32c3, gpio_interrupt:

Before: 850cycles from `handler_interrupts` to the actual handler
After: 125cycles
This commit is contained in:
Scott Mabin 2022-07-26 23:30:50 +01:00
parent 1d02bf87c3
commit de379897a9
2 changed files with 15 additions and 32 deletions

View File

@ -246,7 +246,7 @@ mod vectored {
/// Get the interrupts configured for the core /// Get the interrupts configured for the core
#[inline] #[inline]
fn get_configured_interrupts(_core: Cpu) -> [u128; 15] { fn get_configured_interrupts(_core: Cpu, mut status: u128) -> [u128; 15] {
unsafe { unsafe {
let intr = &*crate::pac::INTERRUPT_CORE0::PTR; let intr = &*crate::pac::INTERRUPT_CORE0::PTR;
let intr_map_base = intr.mac_intr_map.as_ptr(); let intr_map_base = intr.mac_intr_map.as_ptr();
@ -254,8 +254,9 @@ mod vectored {
let mut prios = [0u128; 15]; let mut prios = [0u128; 15];
for i in 0..get_interrupt_count() { while status != 0 {
let i = i as isize; let interrupt_nr = status.trailing_zeros();
let i = interrupt_nr as isize;
let cpu_interrupt = intr_map_base.offset(i).read_volatile(); let cpu_interrupt = intr_map_base.offset(i).read_volatile();
// safety: cast is safe because of repr(u32) // safety: cast is safe because of repr(u32)
let cpu_interrupt: CpuInterrupt = core::mem::transmute(cpu_interrupt); let cpu_interrupt: CpuInterrupt = core::mem::transmute(cpu_interrupt);
@ -264,21 +265,13 @@ mod vectored {
.read_volatile(); .read_volatile();
prios[prio as usize] |= 1 << i; prios[prio as usize] |= 1 << i;
status &= !(1u128 << interrupt_nr);
} }
prios prios
} }
} }
#[inline]
fn get_interrupt_count() -> usize {
cfg_if::cfg_if! {
if #[cfg(feature = "esp32c3")] {
62
}
}
}
/// Interrupt Error /// Interrupt Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error { pub enum Error {
@ -309,7 +302,7 @@ mod vectored {
// so we clear it anyway // so we clear it anyway
clear(crate::get_core(), cpu_intr); clear(crate::get_core(), cpu_intr);
let configured_interrupts = get_configured_interrupts(crate::get_core()); 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[cpu_intr as usize];
while interrupt_mask != 0 { while interrupt_mask != 0 {
let interrupt_nr = interrupt_mask.trailing_zeros(); let interrupt_nr = interrupt_mask.trailing_zeros();

View File

@ -252,7 +252,7 @@ mod vectored {
/// Get the interrupts configured for the core /// Get the interrupts configured for the core
#[inline] #[inline]
fn get_configured_interrupts(core: Cpu) -> [u128; 7] { fn get_configured_interrupts(core: Cpu, mut status: u128) -> [u128; 7] {
unsafe { unsafe {
let intr_map_base = match core { let intr_map_base = match core {
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map.as_ptr(), Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map.as_ptr(),
@ -264,32 +264,22 @@ mod vectored {
let mut levels = [0u128; 7]; let mut levels = [0u128; 7];
for i in 0..get_interrupt_count() { while status != 0 {
let i = i as isize; let interrupt_nr = status.trailing_zeros();
let i = interrupt_nr as isize;
let cpu_interrupt = intr_map_base.offset(i).read_volatile(); let cpu_interrupt = intr_map_base.offset(i).read_volatile();
// safety: cast is safe because of repr(u32) // safety: cast is safe because of repr(u32)
let cpu_interrupt: CpuInterrupt = core::mem::transmute(cpu_interrupt); let cpu_interrupt: CpuInterrupt = core::mem::transmute(cpu_interrupt);
let level = cpu_interrupt.level() as u8 as usize; let level = cpu_interrupt.level() as u8 as usize;
levels[level] |= 1 << i; levels[level] |= 1 << i;
status &= !(1u128 << interrupt_nr);
} }
levels levels
} }
} }
#[inline]
fn get_interrupt_count() -> usize {
cfg_if::cfg_if! {
if #[cfg(feature = "esp32")] {
68
} else if #[cfg(feature = "esp32s2")] {
94
} else if #[cfg(feature = "esp32s3")] {
98
}
}
}
pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> { pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
let cpu_interrupt = let cpu_interrupt =
interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?; interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?;
@ -389,7 +379,8 @@ mod vectored {
handler(level); handler(level);
} }
} else { } else {
let interrupt_levels = get_configured_interrupts(crate::get_core()); let status = get_status(crate::get_core());
let interrupt_levels = get_configured_interrupts(crate::get_core(), status);
if (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 { if (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_EDGE; let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_EDGE;
let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros(); let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
@ -412,8 +403,7 @@ mod vectored {
} else { } else {
// finally check periperal sources and fire of handlers from pac // finally check periperal sources and fire of handlers from pac
// peripheral mapped interrupts are cleared by the peripheral // peripheral mapped interrupts are cleared by the peripheral
let interrupt_mask = let interrupt_mask = status & interrupt_levels[level as usize];
get_status(crate::get_core()) & interrupt_levels[level as usize];
let interrupt_nr = interrupt_mask.trailing_zeros(); let interrupt_nr = interrupt_mask.trailing_zeros();
// Interrupt::try_from can fail if interrupt already de-asserted: // Interrupt::try_from can fail if interrupt already de-asserted: