From c102a329d1a57c34f0c655b1426b39fca244fd57 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Sun, 23 Jan 2022 12:10:45 +0100 Subject: [PATCH] Add basic interrupt support for ESP32C3 and ESP32 --- esp-hal-common/Cargo.toml | 12 +- esp-hal-common/src/gpio.rs | 4 +- esp-hal-common/src/interrupt/riscv.rs | 295 ++++++++++++++++++ esp-hal-common/src/interrupt/xtensa.rs | 144 +++++++++ esp-hal-common/src/lib.rs | 16 +- esp32-hal/.vscode/settings.json | 16 + esp32-hal/build.rs | 7 + esp32-hal/examples/gpio_interrupt.rs | 95 ++++++ esp32-hal/hal-defaults.x | 7 + esp32-hal/src/lib.rs | 9 +- esp32c3-hal/.cargo/config.toml | 3 + esp32c3-hal/.vscode/settings.json | 16 + esp32c3-hal/Cargo.toml | 3 + esp32c3-hal/asm.S | 101 ++++++ esp32c3-hal/asm.bat | 3 + .../bin/asm_riscv32i-unknown-none-elf.a | Bin 0 -> 6334 bytes esp32c3-hal/build.rs | 48 ++- esp32c3-hal/examples/gpio_interrupt.rs | 99 ++++++ esp32c3-hal/hal-defaults.x | 31 ++ esp32c3-hal/src/lib.rs | 8 +- 20 files changed, 907 insertions(+), 10 deletions(-) create mode 100644 esp-hal-common/src/interrupt/riscv.rs create mode 100644 esp-hal-common/src/interrupt/xtensa.rs create mode 100644 esp32-hal/.vscode/settings.json create mode 100644 esp32-hal/examples/gpio_interrupt.rs create mode 100644 esp32-hal/hal-defaults.x create mode 100644 esp32c3-hal/.vscode/settings.json create mode 100644 esp32c3-hal/asm.S create mode 100644 esp32c3-hal/asm.bat create mode 100644 esp32c3-hal/bin/asm_riscv32i-unknown-none-elf.a create mode 100644 esp32c3-hal/examples/gpio_interrupt.rs create mode 100644 esp32c3-hal/hal-defaults.x diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index dfa13ad1a8a..8e40163a9ab 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -17,6 +17,7 @@ paste = "1.0" riscv = { version = "0.7", optional = true } void = { version = "1.0", default-features = false } xtensa-lx = { version = "0.4", optional = true } +xtensa-lx-rt = { version = "0.7", features = ["lx6"], optional = true } procmacros = { path = "../esp-hal-procmacros", package = "esp-hal-procmacros" } # IMPORTANT: # Each supported device MUST have its PAC included below along with a @@ -27,7 +28,10 @@ esp32s2_pac = { package = "esp32s2", git = "https://github.com/jessebraham/esp32 esp32s3_pac = { package = "esp32s3", git = "https://github.com/jessebraham/esp32s3.git", branch = "develop", optional = true } [features] -esp32 = ["esp32_pac", "esp32_pac/rt", "xtensa-lx/lx6", "procmacros/rtc_slow"] -esp32c3 = ["esp32c3_pac", "esp32c3_pac/rt", "riscv"] -esp32s2 = ["esp32s2_pac", "esp32s2_pac/rt", "xtensa-lx/lx6", "procmacros/rtc_slow"] # FIXME -esp32s3 = ["esp32s3_pac", "esp32s3_pac/rt", "xtensa-lx/lx6", "procmacros/rtc_slow"] # FIXME +esp32 = ["esp32_pac", "esp32_pac/rt", "xtensa-lx/lx6", "xtensa-lx-rt/lx6", "procmacros/rtc_slow", "dual_core"] +esp32c3 = ["esp32c3_pac", "esp32c3_pac/rt", "riscv", "single_core"] +esp32s2 = ["esp32s2_pac", "esp32s2_pac/rt", "xtensa-lx/lx6", "xtensa-lx-rt/lx6", "procmacros/rtc_slow", "single_core"] # FIXME +esp32s3 = ["esp32s3_pac", "esp32s3_pac/rt", "xtensa-lx/lx6", "xtensa-lx-rt/lx6", "procmacros/rtc_slow", "dual_core"] # FIXME + +single_core = [] +dual_core = [] diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index 5b57a5c54ec..55a51d40cd9 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -330,10 +330,12 @@ macro_rules! impl_input { _ => {} } } + + // a crate using this macro needs to provide gpio_intr_enable unsafe { (&*GPIO::ptr()).pin[$pin_num].modify(|_, w| w - .pin_int_ena().bits(int_enable as u8 | ((nmi_enable as u8) << 1)) + .pin_int_ena().bits(crate::gpio_intr_enable(int_enable, nmi_enable)) .pin_int_type().bits(event as u8) .pin_wakeup_enable().bit(wake_up_from_light_sleep) ); diff --git a/esp-hal-common/src/interrupt/riscv.rs b/esp-hal-common/src/interrupt/riscv.rs new file mode 100644 index 00000000000..2ac5061eaf4 --- /dev/null +++ b/esp-hal-common/src/interrupt/riscv.rs @@ -0,0 +1,295 @@ +use riscv::register::mcause; + +use crate::{pac::Interrupt, Cpu}; + +// User code shouldn't usually take the mutable TrapFrame or the TrapFrame in +// general. However this makes things like preemtive multitasking easier in +// future +extern "C" { + fn interrupt1(frame: &mut TrapFrame); + fn interrupt2(frame: &mut TrapFrame); + fn interrupt3(frame: &mut TrapFrame); + fn interrupt4(frame: &mut TrapFrame); + fn interrupt5(frame: &mut TrapFrame); + fn interrupt6(frame: &mut TrapFrame); + fn interrupt7(frame: &mut TrapFrame); + fn interrupt8(frame: &mut TrapFrame); + fn interrupt9(frame: &mut TrapFrame); + fn interrupt10(frame: &mut TrapFrame); + fn interrupt11(frame: &mut TrapFrame); + fn interrupt12(frame: &mut TrapFrame); + fn interrupt13(frame: &mut TrapFrame); + fn interrupt14(frame: &mut TrapFrame); + fn interrupt15(frame: &mut TrapFrame); + fn interrupt16(frame: &mut TrapFrame); + fn interrupt17(frame: &mut TrapFrame); + fn interrupt18(frame: &mut TrapFrame); + fn interrupt19(frame: &mut TrapFrame); + fn interrupt20(frame: &mut TrapFrame); + fn interrupt21(frame: &mut TrapFrame); + fn interrupt22(frame: &mut TrapFrame); + fn interrupt23(frame: &mut TrapFrame); + fn interrupt24(frame: &mut TrapFrame); + fn interrupt25(frame: &mut TrapFrame); + fn interrupt26(frame: &mut TrapFrame); + fn interrupt27(frame: &mut TrapFrame); + fn interrupt28(frame: &mut TrapFrame); + fn interrupt29(frame: &mut TrapFrame); + fn interrupt30(frame: &mut TrapFrame); + fn interrupt31(frame: &mut TrapFrame); +} + +/// Interrupt kind +pub enum InterruptKind { + /// Level interrupt + Level, + /// Edge interrupt + Edge, +} + +/// Enumeration of available CPU interrupts. +/// It is possible to create a handler for each of the interrupts. (e.g. `interrupt3`) +pub enum CpuInterrupt { + Interrupt1 = 1, + Interrupt2, + Interrupt3, + Interrupt4, + Interrupt5, + Interrupt6, + Interrupt7, + Interrupt8, + Interrupt9, + Interrupt10, + Interrupt11, + Interrupt12, + Interrupt13, + Interrupt14, + Interrupt15, + Interrupt16, + Interrupt17, + Interrupt18, + Interrupt19, + Interrupt20, + Interrupt21, + Interrupt22, + Interrupt23, + Interrupt24, + Interrupt25, + Interrupt26, + Interrupt27, + Interrupt28, + Interrupt29, + Interrupt30, + Interrupt31, +} + +/// Interrupt priority levels. +pub enum Priority { + None, + Priority1, + Priority2, + Priority3, + Priority4, + Priority5, + Priority6, + Priority7, + Priority8, + Priority9, + Priority10, + Priority11, + Priority12, + Priority13, + Priority14, + Priority15, +} + +/// Enable and assign a peripheral interrupt to an CPU interrupt. +pub fn enable(_core: Cpu, interrupt: Interrupt, which: CpuInterrupt) { + unsafe { + let interrupt_number = interrupt as isize; + let cpu_interrupt_number = which as isize; + let intr = &*crate::pac::INTERRUPT_CORE0::ptr(); + let intr_map_base = intr.mac_intr_map.as_ptr(); + intr_map_base + .offset(interrupt_number) + .write_volatile(cpu_interrupt_number as u32); + + // enable interrupt + intr.cpu_int_enable + .write(|w| w.bits(1 << cpu_interrupt_number)); + } +} + +/// Disable the given peripheral interrupt. +pub fn disable(_core: Cpu, interrupt: Interrupt) { + unsafe { + let interrupt_number = interrupt as isize; + let intr = &*crate::pac::INTERRUPT_CORE0::ptr(); + let intr_map_base = intr.mac_intr_map.as_ptr(); + intr_map_base.offset(interrupt_number).write_volatile(0); + } +} + +/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt +pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) { + unsafe { + let intr = &*crate::pac::INTERRUPT_CORE0::ptr(); + let cpu_interrupt_number = which as isize; + + let interrupt_type = match kind { + InterruptKind::Level => 0, + InterruptKind::Edge => 1, + }; + intr.cpu_int_type.modify(|r, w| { + w.bits( + r.bits() & !(1 << cpu_interrupt_number) | (interrupt_type << cpu_interrupt_number), + ) + }); + } +} + +/// Set the priority level of an CPU interrupt +pub fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) { + unsafe { + let intr = &*crate::pac::INTERRUPT_CORE0::ptr(); + let cpu_interrupt_number = which as isize; + let intr_prio_base = intr.cpu_int_pri_0.as_ptr(); + + intr_prio_base + .offset(cpu_interrupt_number as isize) + .write_volatile(priority as u32); + } +} + +/// Clear a CPU interrupt +pub fn clear(_core: Cpu, which: CpuInterrupt) { + unsafe { + let cpu_interrupt_number = which as isize; + let intr = &*crate::pac::INTERRUPT_CORE0::ptr(); + intr.cpu_int_clear + .write(|w| w.bits(1 << cpu_interrupt_number)); + } +} + +/// Get status of peripheral interrupts +pub fn get_status(_core: Cpu) -> u128 { + unsafe { + ((*crate::pac::INTERRUPT_CORE0::ptr()) + .intr_status_reg_0 + .read() + .bits() as u128) + | ((*crate::pac::INTERRUPT_CORE0::ptr()) + .intr_status_reg_1 + .read() + .bits() as u128) + << 32 + } +} + +// TODO should this be aligned with Atomic Emulation Trap Handler in future? +/// Registers saved in trap handler +#[doc(hidden)] +#[allow(missing_docs)] +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct TrapFrame { + pub ra: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub a4: usize, + pub a5: usize, + pub a6: usize, + pub a7: usize, + pub s0: usize, + pub s1: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub s8: usize, + pub s9: usize, + pub s10: usize, + pub s11: usize, + pub gp: usize, + pub tp: usize, + pub sp: usize, +} + +/// # Safety +/// +/// This function is called from an assembly trap handler. +#[doc(hidden)] +#[link_section = ".trap.rust"] +#[export_name = "_start_trap_rust_hal"] +pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) { + extern "C" { + pub fn _start_trap_rust(trap_frame: *const TrapFrame); + + pub fn DefaultHandler(); + } + + let cause = mcause::read(); + if cause.is_exception() { + _start_trap_rust(trap_frame); + } else { + let code = riscv::register::mcause::read().code(); + match code { + 1 => interrupt1(trap_frame.as_mut().unwrap()), + 2 => interrupt2(trap_frame.as_mut().unwrap()), + 3 => interrupt3(trap_frame.as_mut().unwrap()), + 4 => interrupt4(trap_frame.as_mut().unwrap()), + 5 => interrupt5(trap_frame.as_mut().unwrap()), + 6 => interrupt6(trap_frame.as_mut().unwrap()), + 7 => interrupt7(trap_frame.as_mut().unwrap()), + 8 => interrupt8(trap_frame.as_mut().unwrap()), + 9 => interrupt9(trap_frame.as_mut().unwrap()), + 10 => interrupt10(trap_frame.as_mut().unwrap()), + 11 => interrupt11(trap_frame.as_mut().unwrap()), + 12 => interrupt12(trap_frame.as_mut().unwrap()), + 13 => interrupt13(trap_frame.as_mut().unwrap()), + 14 => interrupt14(trap_frame.as_mut().unwrap()), + 16 => interrupt16(trap_frame.as_mut().unwrap()), + 15 => interrupt15(trap_frame.as_mut().unwrap()), + 17 => interrupt17(trap_frame.as_mut().unwrap()), + 18 => interrupt18(trap_frame.as_mut().unwrap()), + 19 => interrupt19(trap_frame.as_mut().unwrap()), + 20 => interrupt20(trap_frame.as_mut().unwrap()), + 21 => interrupt21(trap_frame.as_mut().unwrap()), + 22 => interrupt22(trap_frame.as_mut().unwrap()), + 23 => interrupt23(trap_frame.as_mut().unwrap()), + 24 => interrupt24(trap_frame.as_mut().unwrap()), + 25 => interrupt25(trap_frame.as_mut().unwrap()), + 26 => interrupt26(trap_frame.as_mut().unwrap()), + 27 => interrupt27(trap_frame.as_mut().unwrap()), + 28 => interrupt28(trap_frame.as_mut().unwrap()), + 29 => interrupt29(trap_frame.as_mut().unwrap()), + 30 => interrupt30(trap_frame.as_mut().unwrap()), + 31 => interrupt31(trap_frame.as_mut().unwrap()), + _ => DefaultHandler(), + }; + } +} + +#[doc(hidden)] +#[no_mangle] +pub fn _setup_interrupts() { + extern "C" { + static _vector_table: *const u32; + } + + unsafe { + let vec_table = &_vector_table as *const _ as usize; + riscv::register::mtvec::write(vec_table, riscv::register::mtvec::TrapMode::Vectored); + }; +} diff --git a/esp-hal-common/src/interrupt/xtensa.rs b/esp-hal-common/src/interrupt/xtensa.rs new file mode 100644 index 00000000000..9d02c376d6c --- /dev/null +++ b/esp-hal-common/src/interrupt/xtensa.rs @@ -0,0 +1,144 @@ +use crate::{pac::Interrupt, Cpu}; + +extern "C" { + fn level1_interrupt(); + fn level2_interrupt(); + fn level3_interrupt(); + fn level4_interrupt(); + fn level5_interrupt(); + fn level6_interrupt(); + fn level7_interrupt(); +} + +/// Enumeration of available CPU interrupts +/// It's possible to create one handler per priority level. (e.g `level1_interrupt`) +#[allow(unused)] +pub enum CpuInterrupt { + Interrupt0LevelPriority1 = 0, + Interrupt1LevelPriority1, + Interrupt2LevelPriority1, + Interrupt3LevelPriority1, + Interrupt4LevelPriority1, + Interrupt5LevelPriority1, + Interrupt6Timer0Priority1, + Interrupt7SoftwarePriority1, + Interrupt8LevelPriority1, + Interrupt9LevelPriority1, + Interrupt10EdgePriority1, + Interrupt11ProfilingPriority3, + Interrupt12LevelPriority1, + Interrupt13LevelPriority1, + Interrupt14NmiPriority7, + Interrupt15Timer1Priority3, + Interrupt16Timer2Priority3, + Interrupt17LevelPriority1, + Interrupt18LevelPriority1, + Interrupt19LevelPriority2, + Interrupt20LevelPriority2, + Interrupt21LevelPriority2, + Interrupt22EdgePriority3, + Interrupt23LevelPriority3, + Interrupt24LevelPriority4, + Interrupt25LevelPriority4, + Interrupt26LevelPriority5, + Interrupt27LevelPriority3, + Interrupt28EdgePriority4, + Interrupt29SoftwarePriority3, + Interrupt30EdgePriority4, + Interrupt31EdgePriority5, +} + +/// Enable and assign a peripheral interrupt to an CPU interrupt. +pub fn enable(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) { + unsafe { + let interrupt_number = interrupt as isize; + let cpu_interrupt_number = which as isize; + let intr = &*crate::pac::DPORT::ptr(); + let intr_map_base = match core { + Cpu::ProCpu => intr.pro_mac_intr_map.as_ptr(), + #[cfg(feature = "dual_core")] + Cpu::AppCpu => intr.app_mac_intr_map.as_ptr(), + #[cfg(feature = "single_core")] + Cpu::AppCpu => intr.pro_mac_intr_map.as_ptr(), + }; + intr_map_base + .offset(interrupt_number) + .write_volatile(cpu_interrupt_number as u32); + } +} + +/// Disable the given peripheral interrupt. +pub fn disable(core: Cpu, interrupt: Interrupt) { + unsafe { + let interrupt_number = interrupt as isize; + let intr = &*crate::pac::DPORT::ptr(); + let intr_map_base = match core { + Cpu::ProCpu => intr.pro_mac_intr_map.as_ptr(), + #[cfg(feature = "dual_core")] + Cpu::AppCpu => intr.app_mac_intr_map.as_ptr(), + #[cfg(feature = "single_core")] + Cpu::AppCpu => intr.pro_mac_intr_map.as_ptr(), + }; + intr_map_base.offset(interrupt_number).write_volatile(0); + } +} + +/// Clear the given CPU interrupt +pub fn clear(_core: Cpu, which: CpuInterrupt) { + unsafe { + xtensa_lx::interrupt::clear(1 << which as u32); + } +} + +/// Get status of peripheral interrupts +pub fn get_status(core: Cpu) -> u128 { + unsafe { + match core { + Cpu::ProCpu => { + ((*crate::pac::DPORT::ptr()).pro_intr_status_0.read().bits() as u128) + | ((*crate::pac::DPORT::ptr()).pro_intr_status_1.read().bits() as u128) << 32 + | ((*crate::pac::DPORT::ptr()).pro_intr_status_2.read().bits() as u128) << 64 + } + Cpu::AppCpu => { + ((*crate::pac::DPORT::ptr()).app_intr_status_0.read().bits() as u128) + | ((*crate::pac::DPORT::ptr()).app_intr_status_1.read().bits() as u128) << 32 + | ((*crate::pac::DPORT::ptr()).app_intr_status_2.read().bits() as u128) << 64 + } + } + } +} + +#[xtensa_lx_rt::interrupt(1)] +fn _level1_interrupt() { + unsafe { level1_interrupt() }; +} + +#[xtensa_lx_rt::interrupt(2)] +fn _level2_interrupt() { + unsafe { level2_interrupt() }; +} + +#[xtensa_lx_rt::interrupt(3)] +fn _level3_interrupt() { + unsafe { level3_interrupt() }; +} + +#[xtensa_lx_rt::interrupt(4)] +fn _level4_interrupt() { + unsafe { level4_interrupt() }; +} + +#[xtensa_lx_rt::interrupt(5)] +fn _level5_interrupt() { + unsafe { level5_interrupt() }; +} + +#[xtensa_lx_rt::interrupt(6)] +fn _level6_interrupt() { + unsafe { level6_interrupt() }; +} + +#[xtensa_lx_rt::interrupt(7)] +fn _level7_interrupt() { + unsafe { level7_interrupt() }; +} diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 32b76427421..6a10207d214 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -29,14 +29,26 @@ pub use esp32s3_pac as pac; pub mod delay; pub mod gpio; +#[cfg_attr(feature = "esp32", path = "interrupt/xtensa.rs")] +#[cfg_attr(feature = "esp32c3", path = "interrupt/riscv.rs")] +pub mod interrupt; pub mod prelude; pub mod serial; pub mod timer; pub use delay::Delay; pub use gpio::*; +pub use interrupt::*; +use procmacros; +pub use procmacros::ram; pub use serial::Serial; pub use timer::Timer; -use procmacros; -pub use procmacros::ram; +/// Enumeration of CPU cores +/// The actual number of available cores depends on the target. +pub enum Cpu { + /// The fist core + ProCpu = 0, + /// The second core + AppCpu, +} diff --git a/esp32-hal/.vscode/settings.json b/esp32-hal/.vscode/settings.json new file mode 100644 index 00000000000..7b7f1b89f41 --- /dev/null +++ b/esp32-hal/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "rust-analyzer.cargo.features": [], + "rust-analyzer.cargo.allFeatures": false, + "editor.formatOnSave": true, + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.allFeatures": false, + "rust-analyzer.cargo.runBuildScripts": false, + "rust-analyzer.checkOnSave.overrideCommand": [ + "cargo", + "check", + "--message-format=json", + "-Z", + "build-std=core", + "--examples" + ] +} \ No newline at end of file diff --git a/esp32-hal/build.rs b/esp32-hal/build.rs index bd5affeecdf..ef0b853979f 100644 --- a/esp32-hal/build.rs +++ b/esp32-hal/build.rs @@ -13,6 +13,13 @@ fn main() { .write_all(include_bytes!("rom.x")) .unwrap(); + File::create(out.join("hal-defaults.x")) + .unwrap() + .write_all(include_bytes!("hal-defaults.x")) + .unwrap(); + + println!("cargo:rustc-link-arg=-Thal-defaults.x"); + println!("cargo:rustc-link-search={}", out.display()); // Only re-run the build script when memory.x is changed, diff --git a/esp32-hal/examples/gpio_interrupt.rs b/esp32-hal/examples/gpio_interrupt.rs new file mode 100644 index 00000000000..01edc2d1bcb --- /dev/null +++ b/esp32-hal/examples/gpio_interrupt.rs @@ -0,0 +1,95 @@ +#![no_std] +#![no_main] + +use core::{cell::RefCell, fmt::Write}; + +use esp32_hal::{ + gpio::{Gpio0, IO}, + pac::{self, Peripherals, UART0}, + prelude::*, + Delay, + Serial, + Timer, +}; +use esp_hal_common::{ + gpio::{Event, Pin}, + interrupt, + Cpu, + Input, + PullDown, +}; +use panic_halt as _; +use xtensa_lx::mutex::{Mutex, SpinLockMutex}; +use xtensa_lx_rt::entry; + +static mut SERIAL: SpinLockMutex>>> = + SpinLockMutex::new(RefCell::new(None)); +static mut BUTTON: SpinLockMutex>>>> = + SpinLockMutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + + // Disable the TIMG watchdog timer. + let mut timer0 = Timer::new(peripherals.TIMG0); + let serial0 = Serial::new(peripherals.UART0).unwrap(); + + timer0.disable(); + + // Set GPIO15 as an output, and set its state high initially. + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = io.pins.gpio15.into_push_pull_output(); + let mut button = io.pins.gpio0.into_pull_down_input(); + button.listen(Event::FallingEdge); + + unsafe { + (&SERIAL).lock(|data| (*data).replace(Some(serial0))); + (&BUTTON).lock(|data| (*data).replace(Some(button))); + } + + interrupt::enable( + Cpu::ProCpu, + pac::Interrupt::GPIO, + interrupt::CpuInterrupt::Interrupt1LevelPriority1, + ); + + led.set_high().unwrap(); + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(); + + unsafe { + xtensa_lx::interrupt::enable(); + } + + loop { + led.toggle().unwrap(); + delay.delay_ms(500u32); + } +} + +#[no_mangle] +pub fn level1_interrupt() { + unsafe { + (&SERIAL).lock(|data| { + let mut serial = data.borrow_mut(); + let serial = serial.as_mut().unwrap(); + writeln!(serial, "Interrupt").ok(); + }); + } + + interrupt::clear( + Cpu::ProCpu, + interrupt::CpuInterrupt::Interrupt1LevelPriority1, + ); + + unsafe { + (&BUTTON).lock(|data| { + let mut button = data.borrow_mut(); + let button = button.as_mut().unwrap(); + button.clear_interrupt(); + }); + } +} diff --git a/esp32-hal/hal-defaults.x b/esp32-hal/hal-defaults.x new file mode 100644 index 00000000000..f58999823fc --- /dev/null +++ b/esp32-hal/hal-defaults.x @@ -0,0 +1,7 @@ +PROVIDE(level1_interrupt = DefaultHandler); +PROVIDE(level2_interrupt = DefaultHandler); +PROVIDE(level3_interrupt = DefaultHandler); +PROVIDE(level4_interrupt = DefaultHandler); +PROVIDE(level5_interrupt = DefaultHandler); +PROVIDE(level6_interrupt = DefaultHandler); +PROVIDE(level7_interrupt = DefaultHandler); diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index 09d9d1cb415..d2564bc22fd 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -7,7 +7,7 @@ pub use self::gpio::IO; pub mod gpio; -pub use esp_hal_common::ram; +pub use esp_hal_common::{interrupt, ram, Cpu}; #[no_mangle] extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {} @@ -55,3 +55,10 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { pub extern "Rust" fn __init_data() -> bool { false } + +fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { + int_enable as u8 + | ((nmi_enable as u8) << 1) + | (int_enable as u8) << 2 + | ((nmi_enable as u8) << 3) +} diff --git a/esp32c3-hal/.cargo/config.toml b/esp32c3-hal/.cargo/config.toml index ff7953215fe..4fb89dfd769 100644 --- a/esp32c3-hal/.cargo/config.toml +++ b/esp32c3-hal/.cargo/config.toml @@ -4,3 +4,6 @@ rustflags = [ [build] target = "riscv32imc-unknown-none-elf" + +[unstable] +build-std = [ "core" ] diff --git a/esp32c3-hal/.vscode/settings.json b/esp32c3-hal/.vscode/settings.json new file mode 100644 index 00000000000..7b7f1b89f41 --- /dev/null +++ b/esp32c3-hal/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "rust-analyzer.cargo.features": [], + "rust-analyzer.cargo.allFeatures": false, + "editor.formatOnSave": true, + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.allFeatures": false, + "rust-analyzer.cargo.runBuildScripts": false, + "rust-analyzer.checkOnSave.overrideCommand": [ + "cargo", + "check", + "--message-format=json", + "-Z", + "build-std=core", + "--examples" + ] +} \ No newline at end of file diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 3b51ede7267..50dab48a886 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -43,6 +43,9 @@ git = "https://github.com/MabezDev/riscv-rt" rev = "6b55e4aa3895924e31bcd151f2f0ab840836fa07" optional = true +[build-dependencies] +riscv-target = "0.1.2" + [dev-dependencies] panic-halt = "0.2" diff --git a/esp32c3-hal/asm.S b/esp32c3-hal/asm.S new file mode 100644 index 00000000000..0bd061f6b35 --- /dev/null +++ b/esp32c3-hal/asm.S @@ -0,0 +1,101 @@ +#define STORE sw +#define LOAD lw +#define LOG_REGBYTES 2 +#define REGBYTES (1 << LOG_REGBYTES) + +/* + Trap entry point (_start_trap) + Saves registers and calls _start_trap_rust_hal, + restores registers and then returns. +*/ +.section .trap, "ax" +.global _start_trap_hal +.option norelax +.align 6 + +_start_trap_hal: + addi sp, sp, -32*REGBYTES + + STORE ra, 0*REGBYTES(sp) + STORE t0, 1*REGBYTES(sp) + STORE t1, 2*REGBYTES(sp) + STORE t2, 3*REGBYTES(sp) + STORE t3, 4*REGBYTES(sp) + STORE t4, 5*REGBYTES(sp) + STORE t5, 6*REGBYTES(sp) + STORE t6, 7*REGBYTES(sp) + STORE a0, 8*REGBYTES(sp) + STORE a1, 9*REGBYTES(sp) + STORE a2, 10*REGBYTES(sp) + STORE a3, 11*REGBYTES(sp) + STORE a4, 12*REGBYTES(sp) + STORE a5, 13*REGBYTES(sp) + STORE a6, 14*REGBYTES(sp) + STORE a7, 15*REGBYTES(sp) + STORE s0, 16*REGBYTES(sp) + STORE s1, 17*REGBYTES(sp) + STORE s2, 18*REGBYTES(sp) + STORE s3, 19*REGBYTES(sp) + STORE s4, 20*REGBYTES(sp) + STORE s5, 21*REGBYTES(sp) + STORE s6, 22*REGBYTES(sp) + STORE s7, 23*REGBYTES(sp) + STORE s8, 24*REGBYTES(sp) + STORE s9, 25*REGBYTES(sp) + STORE s10, 26*REGBYTES(sp) + STORE s11, 27*REGBYTES(sp) + STORE gp, 28*REGBYTES(sp) + STORE tp, 29*REGBYTES(sp) + + addi s0, sp, 32*REGBYTES + STORE s0, 30*REGBYTES(sp) + + add a0, sp, zero + jal ra, _start_trap_rust_hal + + LOAD ra, 0*REGBYTES(sp) + LOAD t0, 1*REGBYTES(sp) + LOAD t1, 2*REGBYTES(sp) + LOAD t2, 3*REGBYTES(sp) + LOAD t3, 4*REGBYTES(sp) + LOAD t4, 5*REGBYTES(sp) + LOAD t5, 6*REGBYTES(sp) + LOAD t6, 7*REGBYTES(sp) + LOAD a0, 8*REGBYTES(sp) + LOAD a1, 9*REGBYTES(sp) + LOAD a2, 10*REGBYTES(sp) + LOAD a3, 11*REGBYTES(sp) + LOAD a4, 12*REGBYTES(sp) + LOAD a5, 13*REGBYTES(sp) + LOAD a6, 14*REGBYTES(sp) + LOAD a7, 15*REGBYTES(sp) + LOAD s0, 16*REGBYTES(sp) + LOAD s1, 17*REGBYTES(sp) + LOAD s2, 18*REGBYTES(sp) + LOAD s3, 19*REGBYTES(sp) + LOAD s4, 20*REGBYTES(sp) + LOAD s5, 21*REGBYTES(sp) + LOAD s6, 22*REGBYTES(sp) + LOAD s7, 23*REGBYTES(sp) + LOAD s8, 24*REGBYTES(sp) + LOAD s9, 25*REGBYTES(sp) + LOAD s10, 26*REGBYTES(sp) + LOAD s11, 27*REGBYTES(sp) + LOAD gp, 28*REGBYTES(sp) + LOAD tp, 29*REGBYTES(sp) + LOAD sp, 30*REGBYTES(sp) + + # SP was restored from the original SP + mret + +.section .trap, "ax" +.balign 0x100 +.global _vector_table +.type _vector_table, @function + +_vector_table: + .option push + .option norvc + .rept 31 + j _start_trap_hal + .endr \ No newline at end of file diff --git a/esp32c3-hal/asm.bat b/esp32c3-hal/asm.bat new file mode 100644 index 00000000000..3951c34a8b7 --- /dev/null +++ b/esp32c3-hal/asm.bat @@ -0,0 +1,3 @@ +riscv32-esp-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32i asm.S -o bin/esp32c3asm.o +riscv32-esp-elf-ar crs bin/asm_riscv32i-unknown-none-elf.a bin/esp32c3asm.o +del bin\esp32c3asm.o diff --git a/esp32c3-hal/bin/asm_riscv32i-unknown-none-elf.a b/esp32c3-hal/bin/asm_riscv32i-unknown-none-elf.a new file mode 100644 index 0000000000000000000000000000000000000000..42d00c2901effe5cd8ecb1da769f406e0ab9313c GIT binary patch literal 6334 zcmd^@eTZCF8OG0Eh;Kh6w*LN!3qWi6)L*4e?$_KdLa?zuP36RR}xV{o2ZyEQP50A;b)UkZXgxWXe!E= zQqj=SR8**>qT<;UkN5C+Pn5gd6Va8PC_kExhDzxzN!=S2n(6VBR)1Q(^p{qkRVyqh z6E#&UT2ZE`b*os?X1J+a!zL((V~@v*{MxyXUUj+)3ov{#^iLel`AF9 zU^CeoTuGWCooWr0Qf5~()!Ma^G6kJ(6-sHdyP0n7UP-qGbdL#3J!YWUQ&+*r7QUqa zZ?(9ZeR*-U-CkV1#8u|Xa)?8(-ELZy)>udiF=lXem*p1rGS6=JC_1(;Uycn>n6&93Z_AyUkDN?3i zo5W2#Wb_-VcR%y%@f+(eGV%G}gr1A9TF8BTja1TIzH!yMO)u+=>Q86%W^MXz>)X+{ zCDk3n9U%L6Js0e~aPOTbmuib2DE#5$2b?CtpLFrg9Rvh0`995;; zd6wFIefGT4xZhK?^W$UlV@o5dS8v$p9^pnd>)u4TPwgk{Bn>-mGb-urVRf<_vbxvP zn>^j`>8+mL=4qTe+wA;7N6P2Pc{=auf~QA3ZQs2x?fm<^@kvkb_w)fzANKT=r;m90 zxThcT^hr-od-{~8=RCdO=_OA$Jbl5_7d^e~>5qH*lb(Lu)1UG5=RN(Tr@!p!r#$_% zr@!XuXFUB)Pk-Cf-}Usfp8lbypZD}tPyg7{KlSv_Jblg6FM0Z9Pyfc#zw`9(J-zDb zKYRK$PygN1Ym0z9|J@Z)o_|lTEh~2XVt1KIc4r0{)w?!zJaS{cJ9RwH?^E8UV@7B7 zkfXcEU=+UeuQlWB3D>Cg3#Q@u+-6kYx;^@Ny?C<|=x5#qEq~u;9#_SwkwD$>`@;FX zQ9D;}{EHtQJPn?s%zH5*H=Z+d!eY|lUWYe1-0$#KhqpPr-QmFDoWpsC3l5JsJmK&@ zhbJB0@9+VK4?8^N@DYcPJN%HtCmo)4_>{wQ4lg*oDKZJA7>8bMgx=h zcfjxAjvJUHa}RUa3s)&HN#-DPh_4jfz$BUbnM1if!6ccZ@a47ylVsipe-n4yz$BUX zGlz29f=M#7@cX#q1}4eOGl#dpRSHa!S!51zLvsU@WX>^%@)H$Il6e&VX70FwNit35 z5MNBVfk`rtF^5~>Dg`FVJi#2wZ(=Y>=5z4nVFQz7z6k$T?zn+TGMAXcZE%$WlVrZa z9KHpvQecwI*O|k&!c_`PlDWbh-Ue4GFiGY+%;9#pN`Xl--)9c-os1ipB=a0|_%^so zfk`qyVh*!#l>(Dwe!?6E+;IbwWPZ*Z4!~6kOaVx#7rDYP#+XSm*WpLj2a{xe3qNOl zFiGYQ@OM}rOp0 zY#vOKxfOoF`e2gG?eKS7A54;o;1{hACdmxKAGSW2Br^8%pUlo)(4Yh?t(vN zeK1L;1b^K6V3N!s_!HI#lVl!%zsLGulFTvqd#w*9$vh1I?bZjAWXkaOSszT2slva* z`e2gGhv467eK1MpEd0By4<^Zc82+U7!6ccF!heVL!6ca${JX6WCdqsX{yo+QlVm;( zf4}v?B$+S3FIgW z2Q3HS_gIeSe79wPo=E1woI7QHRjhyH5&oAaZyPnco$FAedZD3;rz(w#D$dmFs#vQo zREqeAso<&V%(>I$h50kpb;kUe4=nP2@nB_UrdB<_Zn#pboH1=|J#>4nETdXu1 zwfUKI4IcbY8aVLqIP&U_CuclKdaf8_Z%+HIY&1$H`&!CwAQ0YDn&L_5vp z9`_C%E|ZFU2Nz)0~snYHT5chHWAy+bwf$ ong13%D<`<{nZP%3xt9|cNtQ<*S^FC^UhWzGJD!Xaw(A`CpRI=6J^%m! literal 0 HcmV?d00001 diff --git a/esp32c3-hal/build.rs b/esp32c3-hal/build.rs index b992fe241df..cb3059faeda 100644 --- a/esp32c3-hal/build.rs +++ b/esp32c3-hal/build.rs @@ -1,4 +1,11 @@ -use std::{env, fs::File, io::Write, path::PathBuf}; +use std::{ + env, + fs::{self, File}, + io::Write, + path::PathBuf, +}; + +use riscv_target::Target; #[cfg(not(feature = "normalboot"))] fn main() { @@ -26,6 +33,9 @@ fn main() { // instead of when any part of the source code changes. println!("cargo:rerun-if-changed=memory.x"); println!("cargo:rustc-link-arg=-Tesp32c3-link.x"); + + add_defaults(); + prepare_trap(); } #[cfg(feature = "normalboot")] @@ -49,4 +59,40 @@ fn main() { println!("cargo:rerun-if-changed=memory.x"); println!("cargo:rustc-link-arg=-Tmemory.x"); println!("cargo:rustc-link-arg=-Tbl-riscv-link.x"); + + add_defaults(); + prepare_trap(); +} + +fn add_defaults() { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("hal-defaults.x")) + .unwrap() + .write_all(include_bytes!("hal-defaults.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rustc-link-arg=-Thal-defaults.x"); +} + +fn prepare_trap() { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let name = env::var("CARGO_PKG_NAME").unwrap(); + let target = env::var("TARGET").unwrap(); + + if target.starts_with("riscv") { + let mut target = Target::from_target_str(&target); + target.retain_extensions("if"); + + let target = target.to_string(); + + fs::copy( + format!("bin/asm_{}.a", target), + out.join(format!("lib{}.a", name)), + ) + .unwrap(); + + println!("cargo:rustc-link-lib=static={}", name); + println!("cargo:rustc-link-search={}", out.display()); + } } diff --git a/esp32c3-hal/examples/gpio_interrupt.rs b/esp32c3-hal/examples/gpio_interrupt.rs new file mode 100644 index 00000000000..00bf4841310 --- /dev/null +++ b/esp32c3-hal/examples/gpio_interrupt.rs @@ -0,0 +1,99 @@ +#![no_std] +#![no_main] + +use core::{cell::RefCell, fmt::Write}; + +use bare_metal::Mutex; +use esp32c3_hal::{ + gpio::{Gpio9, IO}, + pac::{self, Peripherals, UART0}, + prelude::*, + Delay, + RtcCntl, + Serial, + Timer, +}; +use esp_hal_common::{ + interrupt::{self}, + Cpu, + Event, + Input, + Pin, + PullDown, +}; +use panic_halt as _; +use riscv_rt::entry; + +static mut SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); +static mut BUTTON: Mutex>>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut timer1 = Timer::new(peripherals.TIMG1); + let serial0 = Serial::new(peripherals.UART0).unwrap(); + + rtc_cntl.set_super_wdt_enable(false); + rtc_cntl.set_wdt_enable(false); + timer0.disable(); + timer1.disable(); + + // Set GPIO5 as an output + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = io.pins.gpio5.into_push_pull_output(); + + // Set GPIO9 as an input + let mut button = io.pins.gpio9.into_pull_down_input(); + button.listen(Event::FallingEdge); + + riscv::interrupt::free(|_cs| unsafe { + SERIAL.get_mut().replace(Some(serial0)); + BUTTON.get_mut().replace(Some(button)); + }); + + interrupt::enable( + Cpu::ProCpu, + pac::Interrupt::GPIO, + interrupt::CpuInterrupt::Interrupt3, + ); + interrupt::set_kind( + Cpu::ProCpu, + interrupt::CpuInterrupt::Interrupt3, + interrupt::InterruptKind::Level, + ); + interrupt::set_priority( + Cpu::ProCpu, + interrupt::CpuInterrupt::Interrupt3, + interrupt::Priority::Priority1, + ); + + unsafe { + riscv::interrupt::enable(); + } + + let mut delay = Delay::new(peripherals.SYSTIMER); + loop { + led.toggle().unwrap(); + delay.delay_ms(500u32); + } +} + +#[no_mangle] +pub fn interrupt3() { + riscv::interrupt::free(|cs| unsafe { + let mut serial = SERIAL.borrow(*cs).borrow_mut(); + let serial = serial.as_mut().unwrap(); + let mut button = BUTTON.borrow(*cs).borrow_mut(); + let button = button.as_mut().unwrap(); + + writeln!(serial, "Interrupt").ok(); + + interrupt::clear(Cpu::ProCpu, interrupt::CpuInterrupt::Interrupt3); + button.clear_interrupt(); + }); +} diff --git a/esp32c3-hal/hal-defaults.x b/esp32c3-hal/hal-defaults.x new file mode 100644 index 00000000000..469d127923f --- /dev/null +++ b/esp32c3-hal/hal-defaults.x @@ -0,0 +1,31 @@ +PROVIDE(interrupt1 = DefaultHandler); +PROVIDE(interrupt2 = DefaultHandler); +PROVIDE(interrupt3 = DefaultHandler); +PROVIDE(interrupt4 = DefaultHandler); +PROVIDE(interrupt5 = DefaultHandler); +PROVIDE(interrupt6 = DefaultHandler); +PROVIDE(interrupt7 = DefaultHandler); +PROVIDE(interrupt8 = DefaultHandler); +PROVIDE(interrupt9 = DefaultHandler); +PROVIDE(interrupt10 = DefaultHandler); +PROVIDE(interrupt11 = DefaultHandler); +PROVIDE(interrupt12 = DefaultHandler); +PROVIDE(interrupt13 = DefaultHandler); +PROVIDE(interrupt14 = DefaultHandler); +PROVIDE(interrupt15 = DefaultHandler); +PROVIDE(interrupt16 = DefaultHandler); +PROVIDE(interrupt17 = DefaultHandler); +PROVIDE(interrupt18 = DefaultHandler); +PROVIDE(interrupt19 = DefaultHandler); +PROVIDE(interrupt20 = DefaultHandler); +PROVIDE(interrupt21 = DefaultHandler); +PROVIDE(interrupt22 = DefaultHandler); +PROVIDE(interrupt23 = DefaultHandler); +PROVIDE(interrupt24 = DefaultHandler); +PROVIDE(interrupt25 = DefaultHandler); +PROVIDE(interrupt26 = DefaultHandler); +PROVIDE(interrupt27 = DefaultHandler); +PROVIDE(interrupt28 = DefaultHandler); +PROVIDE(interrupt29 = DefaultHandler); +PROVIDE(interrupt30 = DefaultHandler); +PROVIDE(interrupt31 = DefaultHandler); diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index f5475275b06..23dcb68597f 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -8,7 +8,7 @@ use riscv_rt::pre_init; pub mod gpio; pub mod rtc_cntl; -pub use esp_hal_common::ram; +pub use esp_hal_common::{interrupt, ram, Cpu}; pub use self::{gpio::IO, rtc_cntl::RtcCntl}; @@ -40,6 +40,7 @@ extern "C" { #[cfg(not(feature = "normalboot"))] #[pre_init] #[cfg(not(feature = "normalboot"))] +#[doc(hidden)] unsafe fn init() { r0::init_data(&mut _srwtext, &mut _erwtext, &_irwtext); @@ -54,6 +55,7 @@ unsafe fn init() { #[allow(unreachable_code)] #[export_name = "_mp_hook"] +#[doc(hidden)] pub fn mp_hook() -> bool { unsafe { r0::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end); @@ -69,3 +71,7 @@ pub fn mp_hook() -> bool { false } + +fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { + int_enable as u8 | ((nmi_enable as u8) << 1) +}