diff --git a/esp-hal-common/src/interrupt/riscv.rs b/esp-hal-common/src/interrupt/riscv.rs index ba4bdc75cfd..a8a4042aea5 100644 --- a/esp-hal-common/src/interrupt/riscv.rs +++ b/esp-hal-common/src/interrupt/riscv.rs @@ -166,6 +166,42 @@ mod vectored { } } + /// Similar to [intrinsics::cttz], except we are able to place the lookup + /// table in SRAM + #[link_section = ".rwtext"] + fn ctz(n: u128) -> u32 { + // this isn't quite what LLVM does, they lower cttz to `ctpop(~X & (X-1))`, + // which use some kind of binary-search-like-algorithm plus a lookup + // table + #[inline(always)] + fn ctz32(n: u32) -> u32 { + // cf. https://en.wikipedia.org/wiki/Find_first_set#CTZ + #[link_section = ".data"] + static TABLE: [u8; 32] = { + let mut t = [0u8; 32]; + let mut i = 0; + while i < 32 { + t[((0x077CB531_u32.wrapping_mul(1 << i)) >> 27) as usize] = i; + i += 1; + } + t + }; + + let n = n & (n as i32).wrapping_neg() as u32; // isolate lowest 1 bit + TABLE[(n.wrapping_mul(0x077CB531) >> 27) as usize] as u32 + } + + let n = n.to_ne_bytes(); + let (n, _) = n.as_chunks::<4>(); + for (i, b) in n.iter().enumerate() { + let n = u32::from_ne_bytes(*b); + if n != 0 { + return ctz32(n) + i as u32 * 32; + } + } + u128::BITS + } + /// Get the interrupts configured for the core #[inline] fn get_configured_interrupts(_core: Cpu, mut status: u128) -> [u128; 16] { @@ -173,7 +209,7 @@ mod vectored { let mut prios = [0u128; 16]; while status != 0 { - let interrupt_nr = status.trailing_zeros() as u16; + let interrupt_nr = ctz(status) as u16; // safety: cast is safe because of repr(u16) let cpu_interrupt: CpuInterrupt = get_assigned_cpu_interrupt(core::mem::transmute(interrupt_nr as u16)); @@ -220,9 +256,9 @@ mod vectored { let configured_interrupts = get_configured_interrupts(crate::get_core(), status); let mut interrupt_mask = - status & configured_interrupts[INTERRUPT_TO_PRIORITY[cpu_intr as usize - 1]]; + status & configured_interrupts[interrupt_to_priority(cpu_intr as usize)]; while interrupt_mask != 0 { - let interrupt_nr = interrupt_mask.trailing_zeros(); + let interrupt_nr = ctz(interrupt_mask); // Interrupt::try_from can fail if interrupt already de-asserted: // silently ignore if let Ok(interrupt) = peripherals::Interrupt::try_from(interrupt_nr as u8) { @@ -232,7 +268,7 @@ mod vectored { } } - #[ram] + #[inline(always)] unsafe fn handle_interrupt(interrupt: Interrupt, save_frame: &mut TrapFrame) { extern "C" { // defined in each hal @@ -606,6 +642,11 @@ mod classic { use super::{CpuInterrupt, InterruptKind, Priority}; use crate::Cpu; + #[inline(always)] + pub(super) const fn interrupt_to_priority(irq: usize) -> usize { + irq + } + pub(super) const PRIORITY_TO_INTERRUPT: [usize; 15] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 749170f04bc..8b006146837 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -31,6 +31,8 @@ feature(impl_trait_projections) )] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] +#![feature(const_mut_refs)] +#![feature(slice_as_chunks)] #[cfg(riscv)] pub use esp_riscv_rt::{self, entry, riscv}; @@ -167,6 +169,7 @@ pub enum Cpu { AppCpu, } +#[inline(always)] pub fn get_core() -> Cpu { #[cfg(all(xtensa, multi_core))] match ((xtensa_lx::get_processor_id() >> 13) & 1) != 0 { diff --git a/esp32c3-hal/.cargo/config.toml b/esp32c3-hal/.cargo/config.toml index 922b77a223d..3722ba6b532 100644 --- a/esp32c3-hal/.cargo/config.toml +++ b/esp32c3-hal/.cargo/config.toml @@ -2,6 +2,9 @@ runner = "espflash flash --monitor" rustflags = [ "-C", "link-arg=-Tlinkall.x", + "-C", "force-frame-pointers", + + "-C", "llvm-args=--max-jump-table-size=0", # comment the cfgs below if you do _not_ wish to emulate atomics. # enable the atomic codegen option for RISCV diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 3f13776c4e8..300b2849aa8 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -65,10 +65,13 @@ embassy-time-timg0 = ["esp-hal-common/embassy-time-timg0", "embassy-time/tick-hz interrupt-preemption = ["esp-hal-common/interrupt-preemption"] [profile.dev] -opt-level = 1 +opt-level = 2 +lto = "thin" [profile.release] debug = true +opt-level = 2 +lto = "thin" [[example]] name = "spi_eh1_loopback" @@ -101,3 +104,6 @@ required-features = ["embassy", "async"] [[example]] name = "embassy_i2c" required-features = ["embassy", "async"] + +[patch.crates-io] +esp32c3 = { git = "https://github.com/rustbox/esp-pacs", rev = "c8e7cbb1cef4f6b3fd565c8fbb30e37674657877" } diff --git a/esp32c3-hal/examples/gpio_interrupt.rs b/esp32c3-hal/examples/gpio_interrupt.rs index 880af13882f..5ac9a434b65 100644 --- a/esp32c3-hal/examples/gpio_interrupt.rs +++ b/esp32c3-hal/examples/gpio_interrupt.rs @@ -75,9 +75,15 @@ fn main() -> ! { } #[interrupt] +#[ram] fn GPIO() { critical_section::with(|cs| { - esp_println::println!("GPIO interrupt"); + // can't use println! here, because it delegates to `core::write!` which in turn + // is opaquely "stuck" in Flash + { + use core::fmt::Write; + (esp_println::Printer).write_str("GPIO interrupt\n").ok(); + } BUTTON .borrow_ref_mut(cs) .as_mut() diff --git a/esp32c3-hal/ld/db-riscv-link.x b/esp32c3-hal/ld/db-riscv-link.x index 9a8370dd511..401ba3c3d4b 100644 --- a/esp32c3-hal/ld/db-riscv-link.x +++ b/esp32c3-hal/ld/db-riscv-link.x @@ -55,8 +55,6 @@ SECTIONS KEEP(*(.init.rust)); KEEP(*(.text.abort)); . = ALIGN(4); - KEEP(*(.trap)); - KEEP(*(.trap.rust)); *(.text .text.*); _etext = .; @@ -91,6 +89,8 @@ SECTIONS _data_size = _data_end - _data_start + 8; .rwtext ORIGIN(REGION_RWTEXT) + _data_size : AT(_text_size + _rodata_size + _data_size){ _srwtext = .; + KEEP(*(.trap)); + KEEP(*(.trap.rust)); *(.rwtext); . = ALIGN(4); _erwtext = .; diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 5bd1fbcef9b..8c218846b72 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -16,6 +16,46 @@ pub mod analog { pub use esp_hal_common::analog::{AvailableAnalog, SarAdcExt}; } +mod mem { + #[no_mangle] + #[link_section = ".rwtext"] + pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + let r = dest; + let (n, m) = (n / 4, n % 4); + for i in 0..m { + *dest.add(i) = *src.add(i); + } + let dest = dest.add(m).cast::(); + let src = src.add(m).cast::(); + for i in 0..n { + *dest.add(i) = *src.add(i); + } + r + } + + #[no_mangle] + #[link_section = ".rwtext"] + pub unsafe extern "C" fn memset( + p: *mut u8, + c: i32, // equivalent to a c int + n: usize, + ) -> *mut u8 { + let s = p; + let (n, m) = (n / 4, n % 4); + let b = c as u8; + for i in 0..m { + *p.add(i) = b + } + let p = p.add(m).cast::(); + + let w = usize::from_ne_bytes([b; 4]); + for i in 0..n { + *p.add(i) = w; + } + s + } +} + extern "C" { cfg_if::cfg_if! { if #[cfg(feature = "mcu-boot")] {