mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 04:40:52 +00:00
(re-)introduce the rt
feature on esp-hal (#3706)
Adds the `rt` feature which disables `esp_hal::init` and removes runtime symbols from the global name space. Disabling the `rt` feature is the recommended way to use esp-hal as a library.
This commit is contained in:
parent
978f9083a6
commit
6db771c80e
@ -61,6 +61,7 @@ In general, the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines
|
||||
- see [this example](https://github.com/esp-rs/esp-hal/blob/df2b7bd8472cc1d18db0d9441156575570f59bb3/esp-hal/src/spi/mod.rs#L15)
|
||||
- e.g. `#[cfg_attr(feature = "defmt", derive(defmt::Format))]`
|
||||
- Implementations of common, but unstable traits (e.g. `embassy_embedded_hal::SetConfig`) need to be gated with the `unstable` feature.
|
||||
- Libraries depending on esp-hal, should disable the `rt` feature to avoid future compatibility concerns.
|
||||
|
||||
## API Surface
|
||||
|
||||
|
@ -21,7 +21,7 @@ test = false
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
critical-section = "1.2.0"
|
||||
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal", features = ["requires-unstable"] }
|
||||
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal", default-features = false, features = ["requires-unstable"] }
|
||||
portable-atomic = "1.11.0"
|
||||
static_cell = "2.1.0"
|
||||
|
||||
|
@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Added the feature `requires-unstable` (#3772)
|
||||
- `AnyPin::downcast`/`AnyPeripheral::downcast` to allow retrieving the original GPIO/peripheral type (#3783, #3784)
|
||||
- Add `ESP_HAL_CONFIG_PLACE_RMT_DRIVER_IN_RAM` configuration option to pin the RMT driver in RAM (#3778).
|
||||
- The `rt` feature (#3706)
|
||||
|
||||
### Changed
|
||||
|
||||
@ -35,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Adjusted ESP32-S2 and ESP-S3 memory region lengths to reflect those defined in ESP-IDF. (#3709)
|
||||
- Changed the various `ConfigError` variant names to use a consistent word order. (#3782)
|
||||
- Adjusted ESP32-S2 deep-sleep to hibernate for the Ext1WakeupSource (#3785)
|
||||
- Libraries depending on esp-hal should now disable default features, so that only the final binary crate enables the `rt` feature (#3706)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -83,11 +83,11 @@ esp32s3 = { version = "0.32.0", features = ["critical-section", "rt"], git = "ht
|
||||
|
||||
[target.'cfg(target_arch = "riscv32")'.dependencies]
|
||||
riscv = { version = "0.12.1" }
|
||||
esp-riscv-rt = { version = "0.11.0", path = "../esp-riscv-rt" }
|
||||
esp-riscv-rt = { version = "0.11.0", path = "../esp-riscv-rt", optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "xtensa")'.dependencies]
|
||||
xtensa-lx = { version = "0.11.0", path = "../xtensa-lx" }
|
||||
xtensa-lx-rt = { version = "0.19.0", path = "../xtensa-lx-rt" }
|
||||
xtensa-lx-rt = { version = "0.19.0", path = "../xtensa-lx-rt", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
@ -99,7 +99,7 @@ serde = { version = "1.0.219", default-features = false, features = ["der
|
||||
jiff = { version = "0.2.10", default-features = false, features = ["static"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["rt"]
|
||||
|
||||
# These features are considered private and unstable. They are not covered by
|
||||
# semver guarantees and may change or be removed without notice.
|
||||
@ -174,6 +174,21 @@ esp32s3 = [
|
||||
"esp-metadata-generated/esp32s3",
|
||||
]
|
||||
|
||||
## Runtime support
|
||||
##
|
||||
## If you are depending on `esp-hal` as a library, you should *not* enable the `rt` feature under any circumstance.
|
||||
rt = [
|
||||
"dep:xtensa-lx-rt",
|
||||
"dep:esp-riscv-rt",
|
||||
"esp32?/rt",
|
||||
"esp32c2?/rt",
|
||||
"esp32c3?/rt",
|
||||
"esp32c6?/rt",
|
||||
"esp32h2?/rt",
|
||||
"esp32s2?/rt",
|
||||
"esp32s3?/rt",
|
||||
]
|
||||
|
||||
|
||||
#! ### Logging Feature Flags
|
||||
## Enable logging output using version 0.4 of the `log` crate.
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::error::Error;
|
||||
#[cfg(feature = "rt")]
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
error::Error,
|
||||
fs::{self, File},
|
||||
io::{BufRead, Write},
|
||||
path::{Path, PathBuf},
|
||||
@ -55,11 +56,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Define all necessary configuration symbols for the configured device:
|
||||
chip.define_cfgs();
|
||||
|
||||
// Place all linker scripts in `OUT_DIR`, and instruct Cargo how to find these
|
||||
// files:
|
||||
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// emit config
|
||||
println!("cargo:rerun-if-changed=./esp_config.yml");
|
||||
let cfg_yaml = std::fs::read_to_string("./esp_config.yml")
|
||||
@ -77,18 +73,27 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
if chip.is_xtensa() {
|
||||
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
|
||||
File::create(out.join("memory_extras.x"))?.write_all(&generate_memory_extras())?;
|
||||
// Only emit linker directives if the `rt` feature is enabled
|
||||
#[cfg(feature = "rt")]
|
||||
{
|
||||
// Place all linker scripts in `OUT_DIR`, and instruct Cargo how to find these
|
||||
// files:
|
||||
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
let (irtc, drtc) = if cfg!(feature = "esp32s3") {
|
||||
("rtc_fast_seg", "rtc_fast_seg")
|
||||
} else {
|
||||
("rtc_fast_iram_seg", "rtc_fast_dram_seg")
|
||||
};
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
let alias = format!(
|
||||
r#"
|
||||
if chip.is_xtensa() {
|
||||
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
|
||||
File::create(out.join("memory_extras.x"))?.write_all(&generate_memory_extras())?;
|
||||
|
||||
let (irtc, drtc) = if cfg!(feature = "esp32s3") {
|
||||
("rtc_fast_seg", "rtc_fast_seg")
|
||||
} else {
|
||||
("rtc_fast_iram_seg", "rtc_fast_dram_seg")
|
||||
};
|
||||
|
||||
let alias = format!(
|
||||
r#"
|
||||
REGION_ALIAS("ROTEXT", irom_seg);
|
||||
REGION_ALIAS("RWTEXT", iram_seg);
|
||||
REGION_ALIAS("RODATA", drom_seg);
|
||||
@ -96,38 +101,39 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
REGION_ALIAS("RTC_FAST_RWTEXT", {irtc});
|
||||
REGION_ALIAS("RTC_FAST_RWDATA", {drtc});
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
fs::write(out.join("alias.x"), alias)?;
|
||||
fs::copy("ld/xtensa/hal-defaults.x", out.join("hal-defaults.x"))?;
|
||||
} else {
|
||||
// RISC-V devices:
|
||||
fs::write(out.join("alias.x"), alias)?;
|
||||
fs::copy("ld/xtensa/hal-defaults.x", out.join("hal-defaults.x"))?;
|
||||
} else {
|
||||
// RISC-V devices:
|
||||
|
||||
preprocess_file(
|
||||
&config_symbols,
|
||||
&cfg,
|
||||
"ld/riscv/asserts.x",
|
||||
out.join("asserts.x"),
|
||||
)?;
|
||||
preprocess_file(
|
||||
&config_symbols,
|
||||
&cfg,
|
||||
"ld/riscv/hal-defaults.x",
|
||||
out.join("hal-defaults.x"),
|
||||
)?;
|
||||
preprocess_file(
|
||||
&config_symbols,
|
||||
&cfg,
|
||||
"ld/riscv/asserts.x",
|
||||
out.join("asserts.x"),
|
||||
)?;
|
||||
preprocess_file(
|
||||
&config_symbols,
|
||||
&cfg,
|
||||
"ld/riscv/hal-defaults.x",
|
||||
out.join("hal-defaults.x"),
|
||||
)?;
|
||||
}
|
||||
|
||||
// With the architecture-specific linker scripts taken care of, we can copy all
|
||||
// remaining linker scripts which are common to all devices:
|
||||
copy_dir_all(&config_symbols, &cfg, "ld/sections", &out)?;
|
||||
copy_dir_all(&config_symbols, &cfg, format!("ld/{}", chip.name()), &out)?;
|
||||
}
|
||||
|
||||
// With the architecture-specific linker scripts taken care of, we can copy all
|
||||
// remaining linker scripts which are common to all devices:
|
||||
copy_dir_all(&config_symbols, &cfg, "ld/sections", &out)?;
|
||||
copy_dir_all(&config_symbols, &cfg, format!("ld/{}", chip.name()), &out)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
fn copy_dir_all(
|
||||
config_symbols: &[&str],
|
||||
cfg: &HashMap<String, Value>,
|
||||
@ -158,6 +164,7 @@ fn copy_dir_all(
|
||||
}
|
||||
|
||||
/// A naive pre-processor for linker scripts
|
||||
#[cfg(feature = "rt")]
|
||||
fn preprocess_file(
|
||||
config: &[&str],
|
||||
cfg: &HashMap<String, Value>,
|
||||
@ -200,6 +207,7 @@ fn preprocess_file(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
fn substitute_config(cfg: &HashMap<String, Value>, line: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut chars = line.chars().peekable();
|
||||
@ -237,7 +245,7 @@ fn substitute_config(cfg: &HashMap<String, Value>, line: &str) -> String {
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32")]
|
||||
#[cfg(all(feature = "esp32", feature = "rt"))]
|
||||
fn generate_memory_extras() -> Vec<u8> {
|
||||
let reserve_dram = if cfg!(feature = "__bluetooth") {
|
||||
"0x10000"
|
||||
@ -255,7 +263,7 @@ fn generate_memory_extras() -> Vec<u8> {
|
||||
.to_vec()
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32s2")]
|
||||
#[cfg(all(feature = "esp32s2", feature = "rt"))]
|
||||
fn generate_memory_extras() -> Vec<u8> {
|
||||
let reserved_cache = if cfg!(feature = "psram") {
|
||||
"0x4000"
|
||||
|
@ -43,6 +43,7 @@
|
||||
//! let peripherals = esp_hal::init(config);
|
||||
//! # {after_snippet}
|
||||
//! ```
|
||||
#![cfg_attr(not(feature = "rt"), expect(unused))]
|
||||
|
||||
use core::{cell::Cell, marker::PhantomData};
|
||||
|
||||
|
@ -58,18 +58,17 @@ use portable_atomic::{AtomicPtr, Ordering};
|
||||
use procmacros::ram;
|
||||
use strum::EnumCount;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
use crate::interrupt::{self, DEFAULT_INTERRUPT_HANDLER};
|
||||
use crate::{
|
||||
gpio::{AnyPin, GpioBank, InputPin, set_int_enable},
|
||||
interrupt::{self, DEFAULT_INTERRUPT_HANDLER, Priority},
|
||||
gpio::{AnyPin, GPIO_LOCK, GpioBank, InputPin, set_int_enable},
|
||||
interrupt::Priority,
|
||||
peripherals::{GPIO, Interrupt},
|
||||
sync::RawMutex,
|
||||
};
|
||||
|
||||
/// Convenience constant for `Option::None` pin
|
||||
pub(super) static USER_INTERRUPT_HANDLER: CFnPtr = CFnPtr::new();
|
||||
|
||||
pub(super) static GPIO_LOCK: RawMutex = RawMutex::new();
|
||||
|
||||
pub(super) struct CFnPtr(AtomicPtr<()>);
|
||||
impl CFnPtr {
|
||||
pub const fn new() -> Self {
|
||||
@ -88,6 +87,7 @@ impl CFnPtr {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
pub(crate) fn bind_default_interrupt_handler() {
|
||||
// We first check if a handler is set in the vector table.
|
||||
if let Some(handler) = interrupt::bound_handler(Interrupt::GPIO) {
|
||||
@ -140,6 +140,7 @@ pub(super) fn set_interrupt_priority(interrupt: Interrupt, priority: Priority) {
|
||||
/// status bits unchanged. This enables functions like `is_interrupt_set` to
|
||||
/// work correctly.
|
||||
#[ram]
|
||||
#[cfg(feature = "rt")]
|
||||
extern "C" fn default_gpio_interrupt_handler() {
|
||||
GPIO_LOCK.lock(|| {
|
||||
let banks = interrupt_status();
|
||||
|
@ -68,11 +68,12 @@ crate::unstable_module! {
|
||||
mod asynch;
|
||||
mod embedded_hal_impls;
|
||||
pub(crate) mod interrupt;
|
||||
use interrupt::*;
|
||||
|
||||
mod placeholder;
|
||||
|
||||
use core::fmt::Display;
|
||||
|
||||
use interrupt::*;
|
||||
pub use placeholder::NoPin;
|
||||
use portable_atomic::AtomicU32;
|
||||
use strum::EnumCount;
|
||||
@ -82,10 +83,13 @@ use crate::{
|
||||
interrupt::{InterruptHandler, Priority},
|
||||
peripherals::{GPIO, IO_MUX, Interrupt},
|
||||
private::{self, Sealed},
|
||||
sync::RawMutex,
|
||||
};
|
||||
|
||||
define_io_mux_signals!();
|
||||
|
||||
pub(crate) static GPIO_LOCK: RawMutex = RawMutex::new();
|
||||
|
||||
/// Represents a pin-peripheral connection that, when dropped, disconnects the
|
||||
/// peripheral from the pin.
|
||||
///
|
||||
|
@ -84,6 +84,7 @@ mod xtensa;
|
||||
|
||||
pub mod software;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn EspDefaultHandler(_interrupt: crate::peripherals::Interrupt) {
|
||||
panic!("Unhandled interrupt: {:?}", _interrupt);
|
||||
@ -91,7 +92,18 @@ extern "C" fn EspDefaultHandler(_interrupt: crate::peripherals::Interrupt) {
|
||||
|
||||
/// Default (unhandled) interrupt handler
|
||||
pub const DEFAULT_INTERRUPT_HANDLER: InterruptHandler = InterruptHandler::new(
|
||||
unsafe { core::mem::transmute::<*const (), extern "C" fn()>(EspDefaultHandler as *const ()) },
|
||||
{
|
||||
unsafe extern "C" {
|
||||
fn EspDefaultHandler(_interrupt: crate::peripherals::Interrupt);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
core::mem::transmute::<
|
||||
unsafe extern "C" fn(crate::peripherals::Interrupt),
|
||||
extern "C" fn(),
|
||||
>(EspDefaultHandler)
|
||||
}
|
||||
},
|
||||
Priority::min(),
|
||||
);
|
||||
|
||||
|
@ -12,7 +12,9 @@
|
||||
//! interrupt15() => Priority::Priority15
|
||||
//! ```
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
pub use esp_riscv_rt::TrapFrame;
|
||||
use procmacros::ram;
|
||||
use riscv::register::{mcause, mtvec};
|
||||
|
||||
#[cfg(not(plic))]
|
||||
@ -206,58 +208,6 @@ impl TryFrom<u8> for Priority {
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub static RESERVED_INTERRUPTS: &[usize] = PRIORITY_TO_INTERRUPT;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is called from an assembly trap handler.
|
||||
#[doc(hidden)]
|
||||
#[unsafe(link_section = ".trap.rust")]
|
||||
#[unsafe(export_name = "_start_trap_rust_hal")]
|
||||
pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
|
||||
assert!(
|
||||
mcause::read().is_exception(),
|
||||
"Arrived into _start_trap_rust_hal but mcause is not an exception!"
|
||||
);
|
||||
unsafe extern "C" {
|
||||
fn ExceptionHandler(tf: *mut TrapFrame);
|
||||
}
|
||||
unsafe {
|
||||
ExceptionHandler(trap_frame);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn _setup_interrupts() {
|
||||
unsafe extern "C" {
|
||||
static _vector_table: *const u32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// disable all known interrupts
|
||||
// at least after the 2nd stage bootloader there are some interrupts enabled
|
||||
// (e.g. UART)
|
||||
for peripheral_interrupt in 0..255 {
|
||||
crate::peripherals::Interrupt::try_from(peripheral_interrupt)
|
||||
.map(|intr| {
|
||||
#[cfg(multi_core)]
|
||||
disable(Cpu::AppCpu, intr);
|
||||
disable(Cpu::ProCpu, intr);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
let vec_table = &_vector_table as *const _ as usize;
|
||||
mtvec::write(vec_table, mtvec::TrapMode::Vectored);
|
||||
|
||||
crate::interrupt::init_vectoring();
|
||||
};
|
||||
|
||||
#[cfg(plic)]
|
||||
unsafe {
|
||||
core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable an interrupt by directly binding it to a available CPU interrupt
|
||||
///
|
||||
/// Unless you are sure, you most likely want to use [`enable`] instead.
|
||||
@ -367,8 +317,6 @@ pub(crate) fn bound_cpu_interrupt_for(_cpu: Cpu, interrupt: Interrupt) -> Option
|
||||
}
|
||||
|
||||
mod vectored {
|
||||
use procmacros::ram;
|
||||
|
||||
use super::*;
|
||||
|
||||
// Setup interrupts ready for vectoring
|
||||
@ -394,7 +342,7 @@ mod vectored {
|
||||
/// Get the interrupts configured for the core at the given priority
|
||||
/// matching the given status
|
||||
#[inline]
|
||||
fn configured_interrupts(
|
||||
pub(crate) fn configured_interrupts(
|
||||
core: Cpu,
|
||||
status: InterruptStatus,
|
||||
priority: Priority,
|
||||
@ -467,6 +415,413 @@ mod vectored {
|
||||
Some(addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(plic))]
|
||||
mod classic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::{peripherals::INTERRUPT_CORE0, system::Cpu};
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 0;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
|
||||
&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
// First element is not used, just there to avoid a -1 in the interrupt handler.
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static INTERRUPT_TO_PRIORITY: [u8; 16] =
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
/// Enable a CPU interrupt
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Make sure there is an interrupt handler registered.
|
||||
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_enable()
|
||||
.modify(|r, w| unsafe { w.bits((1 << cpu_interrupt_number) | r.bits()) });
|
||||
}
|
||||
|
||||
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
|
||||
///
|
||||
/// The vectored interrupt handler will take care of clearing edge interrupt
|
||||
/// bits.
|
||||
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
|
||||
unsafe {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
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
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Great care must be taken when using this function; avoid changing the
|
||||
/// priority of interrupts 1 - 15.
|
||||
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_pri(which as usize)
|
||||
.write(|w| unsafe { w.map().bits(priority as u8) });
|
||||
}
|
||||
|
||||
/// Clear a CPU interrupt
|
||||
#[inline]
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
let cpu_interrupt_number = which as usize;
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_clear()
|
||||
.write(|w| w.bits(1 << cpu_interrupt_number));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interrupt priority
|
||||
#[inline]
|
||||
pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
priority(cpu_interrupt)
|
||||
}
|
||||
|
||||
/// Get interrupt priority - called by assembly code
|
||||
#[inline]
|
||||
pub(super) fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
unsafe {
|
||||
core::mem::transmute::<u8, Priority>(
|
||||
intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current run level (the level below which interrupts are masked).
|
||||
pub fn current_runlevel() -> Priority {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits().saturating_sub(1) as u8;
|
||||
|
||||
unwrap!(Priority::try_from(prev_interrupt_priority))
|
||||
}
|
||||
|
||||
/// Changes the current run level (the level below which interrupts are
|
||||
/// masked), and returns the previous run level.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must only be used to raise the runlevel and to restore it
|
||||
/// to a previous value. It must not be used to arbitrarily lower the
|
||||
/// runlevel.
|
||||
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
|
||||
let prev_interrupt_priority = current_runlevel();
|
||||
|
||||
// The CPU responds to interrupts `>= level`, but we want to also disable
|
||||
// interrupts at `level` so we set the threshold to `level + 1`.
|
||||
INTERRUPT_CORE0::regs()
|
||||
.cpu_int_thresh()
|
||||
.write(|w| unsafe { w.bits(level as u32 + 1) });
|
||||
|
||||
prev_interrupt_priority
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
mod rt {
|
||||
use super::*;
|
||||
|
||||
#[unsafe(link_section = ".trap")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
|
||||
// Both C6 and H2 have 5 bits of code. The riscv crate masks 31 bits, which then
|
||||
// causes a bounds check to be present.
|
||||
let interrupt_id: usize = riscv::register::mcause::read().bits() & 0x1f;
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
let interrupt_priority = unsafe {
|
||||
intr.cpu_int_pri(0)
|
||||
.as_ptr()
|
||||
.add(interrupt_id)
|
||||
.read_volatile()
|
||||
};
|
||||
|
||||
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits();
|
||||
if interrupt_priority < 15 {
|
||||
// leave interrupts disabled if interrupt is of max priority.
|
||||
intr.cpu_int_thresh()
|
||||
.write(|w| unsafe { w.bits(interrupt_priority + 1) }); // set the prio threshold to 1 more than current interrupt prio
|
||||
unsafe { riscv::interrupt::enable() };
|
||||
}
|
||||
prev_interrupt_priority
|
||||
}
|
||||
|
||||
#[unsafe(link_section = ".trap")]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn _restore_priority(stored_prio: u32) {
|
||||
riscv::interrupt::disable();
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_thresh()
|
||||
.write(|w| unsafe { w.bits(stored_prio) });
|
||||
}
|
||||
|
||||
/// Globally exported so assembly code can call it.
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
super::priority(cpu_interrupt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(plic)]
|
||||
mod plic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::{peripherals::PLIC_MX, system::Cpu};
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 31;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
|
||||
|
||||
// don't use interrupts reserved for CLIC (0,3,4,7)
|
||||
// for some reason also CPU interrupt 8 doesn't work by default since it's
|
||||
// disabled after reset - so don't use that, too
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
|
||||
&[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
|
||||
|
||||
// First element is not used, just there to avoid a -1 in the interrupt handler.
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static INTERRUPT_TO_PRIORITY: [u8; 20] = [
|
||||
0, 1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
];
|
||||
|
||||
/// Enable a CPU interrupt
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Make sure there is an interrupt handler registered.
|
||||
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
|
||||
unsafe {
|
||||
PLIC_MX::regs().mxint_enable().modify(|r, w| {
|
||||
let old = r.cpu_mxint_enable().bits();
|
||||
let new = old | (1 << (which as isize));
|
||||
w.cpu_mxint_enable().bits(new)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
|
||||
///
|
||||
/// The vectored interrupt handler will take care of clearing edge interrupt
|
||||
/// bits.
|
||||
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
|
||||
let interrupt_type = match kind {
|
||||
InterruptKind::Level => 0,
|
||||
InterruptKind::Edge => 1,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
PLIC_MX::regs().mxint_type().modify(|r, w| {
|
||||
let old = r.cpu_mxint_type().bits();
|
||||
let new = old & !(1 << (which as isize)) | (interrupt_type << (which as isize));
|
||||
w.cpu_mxint_type().bits(new)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the priority level of an CPU interrupt
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Great care must be taken when using this function; avoid changing the
|
||||
/// priority of interrupts 1 - 15.
|
||||
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
|
||||
unsafe {
|
||||
PLIC_MX::regs()
|
||||
.mxint_pri(which as usize)
|
||||
.modify(|_, w| w.cpu_mxint_pri().bits(priority as u8));
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear a CPU interrupt
|
||||
#[inline]
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
PLIC_MX::regs().mxint_clear().modify(|r, w| {
|
||||
let old = r.cpu_mxint_clear().bits();
|
||||
let new = old | (1 << (which as isize));
|
||||
w.cpu_mxint_clear().bits(new)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interrupt priority for the CPU
|
||||
#[inline]
|
||||
pub fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
priority(cpu_interrupt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Get interrupt priority.
|
||||
pub fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let prio = PLIC_MX::regs()
|
||||
.mxint_pri(cpu_interrupt as usize)
|
||||
.read()
|
||||
.cpu_mxint_pri()
|
||||
.bits();
|
||||
unsafe { core::mem::transmute::<u8, Priority>(prio) }
|
||||
}
|
||||
|
||||
/// Get the current run level (the level below which interrupts are masked).
|
||||
pub fn current_runlevel() -> Priority {
|
||||
let prev_interrupt_priority = PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.read()
|
||||
.cpu_mxint_thresh()
|
||||
.bits()
|
||||
.saturating_sub(1);
|
||||
|
||||
unwrap!(Priority::try_from(prev_interrupt_priority))
|
||||
}
|
||||
|
||||
/// Changes the current run level (the level below which interrupts are
|
||||
/// masked), and returns the previous run level.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must only be used to raise the runlevel and to restore it
|
||||
/// to a previous value. It must not be used to arbitrarily lower the
|
||||
/// runlevel.
|
||||
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
|
||||
let prev_interrupt_priority = current_runlevel();
|
||||
|
||||
// The CPU responds to interrupts `>= level`, but we want to also disable
|
||||
// interrupts at `level` so we set the threshold to `level + 1`.
|
||||
PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.write(|w| unsafe { w.cpu_mxint_thresh().bits(level as u8 + 1) });
|
||||
|
||||
prev_interrupt_priority
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
mod rt {
|
||||
use super::*;
|
||||
/// Get interrupt priority - called by assembly code
|
||||
#[unsafe(no_mangle)]
|
||||
pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
super::priority(cpu_interrupt)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".trap")]
|
||||
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
|
||||
let interrupt_id: usize = riscv::register::mcause::read().code(); // MSB is whether its exception or interrupt.
|
||||
let interrupt_priority = PLIC_MX::regs()
|
||||
.mxint_pri(interrupt_id)
|
||||
.read()
|
||||
.cpu_mxint_pri()
|
||||
.bits();
|
||||
|
||||
let prev_interrupt_priority = PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.read()
|
||||
.cpu_mxint_thresh()
|
||||
.bits();
|
||||
if interrupt_priority < 15 {
|
||||
// leave interrupts disabled if interrupt is of max priority.
|
||||
PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.write(|w| unsafe { w.cpu_mxint_thresh().bits(interrupt_priority + 1) });
|
||||
|
||||
unsafe {
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
prev_interrupt_priority as u32
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".trap")]
|
||||
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
|
||||
riscv::interrupt::disable();
|
||||
PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.write(|w| unsafe { w.cpu_mxint_thresh().bits(stored_prio as u8) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
mod rt {
|
||||
use esp_riscv_rt::TrapFrame;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is called from an assembly trap handler.
|
||||
#[doc(hidden)]
|
||||
#[unsafe(link_section = ".trap.rust")]
|
||||
#[unsafe(export_name = "_start_trap_rust_hal")]
|
||||
unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
|
||||
assert!(
|
||||
mcause::read().is_exception(),
|
||||
"Arrived into _start_trap_rust_hal but mcause is not an exception!"
|
||||
);
|
||||
unsafe extern "C" {
|
||||
fn ExceptionHandler(tf: *mut TrapFrame);
|
||||
}
|
||||
unsafe {
|
||||
ExceptionHandler(trap_frame);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe fn _setup_interrupts() {
|
||||
unsafe extern "C" {
|
||||
static _vector_table: *const u32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// disable all known interrupts
|
||||
// at least after the 2nd stage bootloader there are some interrupts enabled
|
||||
// (e.g. UART)
|
||||
for peripheral_interrupt in 0..255 {
|
||||
crate::peripherals::Interrupt::try_from(peripheral_interrupt)
|
||||
.map(|intr| {
|
||||
#[cfg(multi_core)]
|
||||
disable(Cpu::AppCpu, intr);
|
||||
disable(Cpu::ProCpu, intr);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
let vec_table = &_vector_table as *const _ as usize;
|
||||
mtvec::write(vec_table, mtvec::TrapMode::Vectored);
|
||||
|
||||
crate::interrupt::init_vectoring();
|
||||
};
|
||||
|
||||
#[cfg(plic)]
|
||||
unsafe {
|
||||
core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[ram]
|
||||
@ -480,7 +835,7 @@ mod vectored {
|
||||
|
||||
let priority = INTERRUPT_TO_PRIORITY[cpu_intr as usize];
|
||||
let prio: Priority = unsafe { core::mem::transmute(priority) };
|
||||
let configured_interrupts = configured_interrupts(core, status, prio);
|
||||
let configured_interrupts = vectored::configured_interrupts(core, status, prio);
|
||||
|
||||
for interrupt_nr in configured_interrupts.iterator() {
|
||||
// Don't use `Interrupt::try_from`. It's slower and placed in flash
|
||||
@ -582,330 +937,3 @@ mod vectored {
|
||||
#[cfg(plic)]
|
||||
interrupt_handler!(19);
|
||||
}
|
||||
|
||||
#[cfg(not(plic))]
|
||||
mod classic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::{peripherals::INTERRUPT_CORE0, system::Cpu};
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 0;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
|
||||
&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
// First element is not used, just there to avoid a -1 in the interrupt handler.
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static INTERRUPT_TO_PRIORITY: [u8; 16] =
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
/// Enable a CPU interrupt
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Make sure there is an interrupt handler registered.
|
||||
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_enable()
|
||||
.modify(|r, w| unsafe { w.bits((1 << cpu_interrupt_number) | r.bits()) });
|
||||
}
|
||||
|
||||
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
|
||||
///
|
||||
/// The vectored interrupt handler will take care of clearing edge interrupt
|
||||
/// bits.
|
||||
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
|
||||
unsafe {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
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
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Great care must be taken when using this function; avoid changing the
|
||||
/// priority of interrupts 1 - 15.
|
||||
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_pri(which as usize)
|
||||
.write(|w| unsafe { w.map().bits(priority as u8) });
|
||||
}
|
||||
|
||||
/// Clear a CPU interrupt
|
||||
#[inline]
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
let cpu_interrupt_number = which as usize;
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_clear()
|
||||
.write(|w| w.bits(1 << cpu_interrupt_number));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interrupt priority
|
||||
#[inline]
|
||||
pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
unsafe { priority(cpu_interrupt) }
|
||||
}
|
||||
|
||||
/// Get interrupt priority - called by assembly code
|
||||
#[inline]
|
||||
pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
unsafe {
|
||||
core::mem::transmute::<u8, Priority>(
|
||||
intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(),
|
||||
)
|
||||
}
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".trap")]
|
||||
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
|
||||
use super::mcause;
|
||||
// Both C6 and H2 have 5 bits of code. The riscv crate masks 31 bits, which then
|
||||
// causes a bounds check to be present.
|
||||
let interrupt_id: usize = mcause::read().bits() & 0x1f;
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
let interrupt_priority = unsafe {
|
||||
intr.cpu_int_pri(0)
|
||||
.as_ptr()
|
||||
.add(interrupt_id)
|
||||
.read_volatile()
|
||||
};
|
||||
|
||||
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits();
|
||||
if interrupt_priority < 15 {
|
||||
// leave interrupts disabled if interrupt is of max priority.
|
||||
intr.cpu_int_thresh()
|
||||
.write(|w| unsafe { w.bits(interrupt_priority + 1) }); // set the prio threshold to 1 more than current interrupt prio
|
||||
unsafe { riscv::interrupt::enable() };
|
||||
}
|
||||
prev_interrupt_priority
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".trap")]
|
||||
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
|
||||
riscv::interrupt::disable();
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
intr.cpu_int_thresh()
|
||||
.write(|w| unsafe { w.bits(stored_prio) });
|
||||
}
|
||||
|
||||
/// Get the current run level (the level below which interrupts are masked).
|
||||
pub fn current_runlevel() -> Priority {
|
||||
let intr = INTERRUPT_CORE0::regs();
|
||||
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits().saturating_sub(1) as u8;
|
||||
|
||||
unwrap!(Priority::try_from(prev_interrupt_priority))
|
||||
}
|
||||
|
||||
/// Changes the current run level (the level below which interrupts are
|
||||
/// masked), and returns the previous run level.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must only be used to raise the runlevel and to restore it
|
||||
/// to a previous value. It must not be used to arbitrarily lower the
|
||||
/// runlevel.
|
||||
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
|
||||
let prev_interrupt_priority = current_runlevel();
|
||||
|
||||
// The CPU responds to interrupts `>= level`, but we want to also disable
|
||||
// interrupts at `level` so we set the threshold to `level + 1`.
|
||||
INTERRUPT_CORE0::regs()
|
||||
.cpu_int_thresh()
|
||||
.write(|w| unsafe { w.bits(level as u32 + 1) });
|
||||
|
||||
prev_interrupt_priority
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(plic)]
|
||||
mod plic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::{peripherals::PLIC_MX, system::Cpu};
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 31;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
|
||||
|
||||
// don't use interrupts reserved for CLIC (0,3,4,7)
|
||||
// for some reason also CPU interrupt 8 doesn't work by default since it's
|
||||
// disabled after reset - so don't use that, too
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
|
||||
&[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
|
||||
|
||||
// First element is not used, just there to avoid a -1 in the interrupt handler.
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub(super) static INTERRUPT_TO_PRIORITY: [u8; 20] = [
|
||||
0, 1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
];
|
||||
|
||||
/// Enable a CPU interrupt
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Make sure there is an interrupt handler registered.
|
||||
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
|
||||
unsafe {
|
||||
PLIC_MX::regs().mxint_enable().modify(|r, w| {
|
||||
let old = r.cpu_mxint_enable().bits();
|
||||
let new = old | (1 << (which as isize));
|
||||
w.cpu_mxint_enable().bits(new)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
|
||||
///
|
||||
/// The vectored interrupt handler will take care of clearing edge interrupt
|
||||
/// bits.
|
||||
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
|
||||
let interrupt_type = match kind {
|
||||
InterruptKind::Level => 0,
|
||||
InterruptKind::Edge => 1,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
PLIC_MX::regs().mxint_type().modify(|r, w| {
|
||||
let old = r.cpu_mxint_type().bits();
|
||||
let new = old & !(1 << (which as isize)) | (interrupt_type << (which as isize));
|
||||
w.cpu_mxint_type().bits(new)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the priority level of an CPU interrupt
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Great care must be taken when using this function; avoid changing the
|
||||
/// priority of interrupts 1 - 15.
|
||||
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
|
||||
unsafe {
|
||||
PLIC_MX::regs()
|
||||
.mxint_pri(which as usize)
|
||||
.modify(|_, w| w.cpu_mxint_pri().bits(priority as u8));
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear a CPU interrupt
|
||||
#[inline]
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
PLIC_MX::regs().mxint_clear().modify(|r, w| {
|
||||
let old = r.cpu_mxint_clear().bits();
|
||||
let new = old | (1 << (which as isize));
|
||||
w.cpu_mxint_clear().bits(new)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interrupt priority
|
||||
#[inline]
|
||||
pub(super) unsafe extern "C" fn priority_by_core(
|
||||
_core: Cpu,
|
||||
cpu_interrupt: CpuInterrupt,
|
||||
) -> Priority {
|
||||
unsafe { priority(cpu_interrupt) }
|
||||
}
|
||||
|
||||
/// Get interrupt priority - called by assembly code
|
||||
#[inline]
|
||||
pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let prio = PLIC_MX::regs()
|
||||
.mxint_pri(cpu_interrupt as usize)
|
||||
.read()
|
||||
.cpu_mxint_pri()
|
||||
.bits();
|
||||
unsafe { core::mem::transmute::<u8, Priority>(prio) }
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".trap")]
|
||||
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
|
||||
let interrupt_id: usize = super::mcause::read().code(); // MSB is whether its exception or interrupt.
|
||||
let interrupt_priority = PLIC_MX::regs()
|
||||
.mxint_pri(interrupt_id)
|
||||
.read()
|
||||
.cpu_mxint_pri()
|
||||
.bits();
|
||||
|
||||
let prev_interrupt_priority = PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.read()
|
||||
.cpu_mxint_thresh()
|
||||
.bits();
|
||||
if interrupt_priority < 15 {
|
||||
// leave interrupts disabled if interrupt is of max priority.
|
||||
PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.write(|w| unsafe { w.cpu_mxint_thresh().bits(interrupt_priority + 1) });
|
||||
|
||||
unsafe {
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
prev_interrupt_priority as u32
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".trap")]
|
||||
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
|
||||
riscv::interrupt::disable();
|
||||
PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.write(|w| unsafe { w.cpu_mxint_thresh().bits(stored_prio as u8) });
|
||||
}
|
||||
|
||||
/// Get the current run level (the level below which interrupts are masked).
|
||||
pub fn current_runlevel() -> Priority {
|
||||
let prev_interrupt_priority = PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.read()
|
||||
.cpu_mxint_thresh()
|
||||
.bits()
|
||||
.saturating_sub(1);
|
||||
|
||||
unwrap!(Priority::try_from(prev_interrupt_priority))
|
||||
}
|
||||
|
||||
/// Changes the current run level (the level below which interrupts are
|
||||
/// masked), and returns the previous run level.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must only be used to raise the runlevel and to restore it
|
||||
/// to a previous value. It must not be used to arbitrarily lower the
|
||||
/// runlevel.
|
||||
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
|
||||
let prev_interrupt_priority = current_runlevel();
|
||||
|
||||
// The CPU responds to interrupts `>= level`, but we want to also disable
|
||||
// interrupts at `level` so we set the threshold to `level + 1`.
|
||||
PLIC_MX::regs()
|
||||
.mxint_thresh()
|
||||
.write(|w| unsafe { w.cpu_mxint_thresh().bits(level as u8 + 1) });
|
||||
|
||||
prev_interrupt_priority
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
//! Interrupt handling
|
||||
|
||||
use procmacros::ram;
|
||||
use xtensa_lx::interrupt;
|
||||
#[cfg(esp32)]
|
||||
pub(crate) use xtensa_lx::interrupt::free;
|
||||
#[cfg(feature = "rt")]
|
||||
use xtensa_lx_rt::exception::Context;
|
||||
|
||||
pub use self::vectored::*;
|
||||
@ -368,8 +370,6 @@ pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
|
||||
}
|
||||
|
||||
mod vectored {
|
||||
use procmacros::ram;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Interrupt priority levels.
|
||||
@ -468,7 +468,11 @@ mod vectored {
|
||||
|
||||
/// Get the interrupts configured for the core
|
||||
#[inline(always)]
|
||||
fn configured_interrupts(core: Cpu, status: InterruptStatus, level: u32) -> InterruptStatus {
|
||||
pub(crate) fn configured_interrupts(
|
||||
core: Cpu,
|
||||
status: InterruptStatus,
|
||||
level: u32,
|
||||
) -> InterruptStatus {
|
||||
unsafe {
|
||||
let intr_map_base = match core {
|
||||
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
|
||||
@ -567,7 +571,7 @@ mod vectored {
|
||||
|
||||
// TODO use CpuInterrupt::LevelX.mask() // TODO make it const
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
static CPU_INTERRUPT_LEVELS: [u32; 8] = [
|
||||
pub(crate) static CPU_INTERRUPT_LEVELS: [u32; 8] = [
|
||||
0b_0000_0000_0000_0000_0000_0000_0000_0000, // Dummy level 0
|
||||
0b_0000_0000_0000_0110_0011_0111_1111_1111, // Level_1
|
||||
0b_0000_0000_0011_1000_0000_0000_0000_0000, // Level 2
|
||||
@ -578,28 +582,79 @@ mod vectored {
|
||||
0b_0000_0000_0000_0000_0100_0000_0000_0000, // Level 7
|
||||
];
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
|
||||
pub(crate) static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
|
||||
pub(crate) static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
|
||||
|
||||
#[inline]
|
||||
fn cpu_interrupt_nr_to_cpu_interrupt_handler(
|
||||
number: u32,
|
||||
) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
|
||||
use xtensa_lx_rt::*;
|
||||
// we're fortunate that all esp variants use the same CPU interrupt layout
|
||||
Some(match number {
|
||||
6 => Timer0,
|
||||
7 => Software0,
|
||||
11 => Profiling,
|
||||
14 => NMI,
|
||||
15 => Timer1,
|
||||
16 => Timer2,
|
||||
29 => Software1,
|
||||
_ => return None,
|
||||
})
|
||||
#[cfg(esp32)]
|
||||
pub(crate) mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b1111_1100_0000_0000_0000_0000_0000_0000,
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0011,
|
||||
);
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
[
|
||||
Interrupt::TG0_T0_EDGE,
|
||||
Interrupt::TG0_T1_EDGE,
|
||||
Interrupt::TG0_WDT_EDGE,
|
||||
Interrupt::TG0_LACT_EDGE,
|
||||
Interrupt::TG1_T0_EDGE,
|
||||
Interrupt::TG1_T1_EDGE,
|
||||
Interrupt::TG1_WDT_EDGE,
|
||||
Interrupt::TG1_LACT_EDGE,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
pub(crate) mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b1100_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b0000_0000_0000_0000_0000_0011_1011_1111,
|
||||
);
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
[
|
||||
Interrupt::TG0_T0_EDGE,
|
||||
Interrupt::TG0_T1_EDGE,
|
||||
Interrupt::TG0_WDT_EDGE,
|
||||
Interrupt::TG0_LACT_EDGE,
|
||||
Interrupt::TG1_T0_EDGE,
|
||||
Interrupt::TG1_T1_EDGE,
|
||||
Interrupt::TG1_WDT_EDGE,
|
||||
Interrupt::TG1_LACT_EDGE,
|
||||
Interrupt::SYSTIMER_TARGET0,
|
||||
Interrupt::SYSTIMER_TARGET1,
|
||||
Interrupt::SYSTIMER_TARGET2,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
pub(crate) mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::empty();
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
mod rt {
|
||||
use super::{vectored::*, *};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[ram]
|
||||
unsafe fn __level_1_interrupt(save_frame: &mut Context) {
|
||||
@ -696,74 +751,25 @@ mod vectored {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b1111_1100_0000_0000_0000_0000_0000_0000,
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0011,
|
||||
);
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
[
|
||||
Interrupt::TG0_T0_EDGE,
|
||||
Interrupt::TG0_T1_EDGE,
|
||||
Interrupt::TG0_WDT_EDGE,
|
||||
Interrupt::TG0_LACT_EDGE,
|
||||
Interrupt::TG1_T0_EDGE,
|
||||
Interrupt::TG1_T1_EDGE,
|
||||
Interrupt::TG1_WDT_EDGE,
|
||||
Interrupt::TG1_LACT_EDGE,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn cpu_interrupt_nr_to_cpu_interrupt_handler(
|
||||
number: u32,
|
||||
) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
|
||||
use xtensa_lx_rt::*;
|
||||
// we're fortunate that all esp variants use the same CPU interrupt layout
|
||||
Some(match number {
|
||||
6 => Timer0,
|
||||
7 => Software0,
|
||||
11 => Profiling,
|
||||
14 => NMI,
|
||||
15 => Timer1,
|
||||
16 => Timer2,
|
||||
29 => Software1,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b1100_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b0000_0000_0000_0000_0000_0011_1011_1111,
|
||||
);
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
[
|
||||
Interrupt::TG0_T0_EDGE,
|
||||
Interrupt::TG0_T1_EDGE,
|
||||
Interrupt::TG0_WDT_EDGE,
|
||||
Interrupt::TG0_LACT_EDGE,
|
||||
Interrupt::TG1_T0_EDGE,
|
||||
Interrupt::TG1_T1_EDGE,
|
||||
Interrupt::TG1_WDT_EDGE,
|
||||
Interrupt::TG1_LACT_EDGE,
|
||||
Interrupt::SYSTIMER_TARGET0,
|
||||
Interrupt::SYSTIMER_TARGET1,
|
||||
Interrupt::SYSTIMER_TARGET2,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::empty();
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod raw {
|
||||
use super::*;
|
||||
|
||||
// Raw handlers for CPU interrupts, assembly only.
|
||||
unsafe extern "C" {
|
||||
fn level4_interrupt(save_frame: &mut Context);
|
||||
fn level5_interrupt(save_frame: &mut Context);
|
||||
|
@ -160,6 +160,23 @@
|
||||
//!
|
||||
//! You might want to consider using [`#[deny(clippy::mem_forget)`](https://rust-lang.github.io/rust-clippy/v0.0.212/index.html#mem_forget) in your project.
|
||||
//!
|
||||
//! ## Library usage
|
||||
//!
|
||||
//! If you intend to write a library that uses esp-hal, you should import it as follows:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! esp-hal = { version = "1", default-features = false } }
|
||||
//! ```
|
||||
//!
|
||||
//! This ensures that the `rt` feature is not enabled, nor any chip features. The application that
|
||||
//! uses your library will then be able to choose the chip feature it needs and enable `rt` such
|
||||
//! that only the final user application calls [`init`].
|
||||
//!
|
||||
//! If your library depends on `unstable` features, you *must* use the `requires-unstable` feature,
|
||||
//! and *not* the unstable feature itself. Doing so, improves the quality of the error messages if a
|
||||
//! user hasn't enabled the unstable feature of esp-hal.
|
||||
//!
|
||||
//! [documentation]: https://docs.espressif.com/projects/rust/esp-hal/latest/
|
||||
//! [examples]: https://github.com/esp-rs/esp-hal/tree/main/examples
|
||||
//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
|
||||
@ -192,13 +209,14 @@ use esp_rom_sys as _;
|
||||
|
||||
metadata!("build_info", CHIP_NAME, chip!());
|
||||
|
||||
#[cfg(riscv)]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[cfg(all(riscv, feature = "rt"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
|
||||
#[cfg_attr(not(feature = "unstable"), doc(hidden))]
|
||||
pub use esp_riscv_rt::{self, riscv};
|
||||
pub(crate) use peripherals::pac;
|
||||
#[cfg(xtensa)]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[cfg(all(xtensa, feature = "rt"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "rt"))))]
|
||||
#[cfg_attr(not(feature = "unstable"), doc(hidden))]
|
||||
pub use xtensa_lx_rt::{self, xtensa_lx};
|
||||
|
||||
@ -236,6 +254,7 @@ pub mod uart;
|
||||
|
||||
mod macros;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
pub use procmacros::blocking_main as main;
|
||||
#[cfg(any(lp_core, ulp_riscv_core))]
|
||||
#[cfg(feature = "unstable")]
|
||||
@ -356,6 +375,7 @@ unstable_driver! {
|
||||
|
||||
/// State of the CPU saved when entering exception or interrupt
|
||||
#[instability::unstable]
|
||||
#[cfg(feature = "rt")]
|
||||
#[allow(unused_imports)]
|
||||
pub mod trapframe {
|
||||
#[cfg(riscv)]
|
||||
@ -548,18 +568,19 @@ pub mod __macro_implementation {
|
||||
#[instability::unstable]
|
||||
pub const fn assert_is_persistable<T: super::Persistable>() {}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(riscv)]
|
||||
pub use esp_riscv_rt::entry as __entry;
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(xtensa)]
|
||||
pub use xtensa_lx_rt::entry as __entry;
|
||||
}
|
||||
|
||||
use crate::clock::CpuClock;
|
||||
#[cfg(feature = "unstable")]
|
||||
use crate::config::{WatchdogConfig, WatchdogStatus};
|
||||
use crate::{
|
||||
clock::{Clocks, CpuClock},
|
||||
peripherals::Peripherals,
|
||||
};
|
||||
#[cfg(feature = "rt")]
|
||||
use crate::{clock::Clocks, peripherals::Peripherals};
|
||||
|
||||
/// System configuration.
|
||||
///
|
||||
@ -603,6 +624,8 @@ pub struct Config {
|
||||
/// let peripherals = init(Config::default());
|
||||
/// # {after_snippet}
|
||||
/// ```
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
|
||||
#[cfg(feature = "rt")]
|
||||
pub fn init(config: Config) -> Peripherals {
|
||||
crate::soc::pre_init();
|
||||
|
||||
|
@ -299,6 +299,7 @@ macro_rules! ignore {
|
||||
#[doc(hidden)]
|
||||
macro_rules! metadata {
|
||||
($category:literal, $key:ident, $value:expr) => {
|
||||
#[cfg(feature = "rt")]
|
||||
#[unsafe(link_section = concat!(".espressif.metadata"))]
|
||||
#[used]
|
||||
#[unsafe(export_name = concat!($category, ".", stringify!($key)))]
|
||||
|
@ -190,6 +190,7 @@ for_each_peripheral! {
|
||||
impl Peripherals {
|
||||
/// Returns all the peripherals *once*
|
||||
#[inline]
|
||||
#[cfg_attr(not(feature = "rt"), expect(dead_code))]
|
||||
pub(crate) fn take() -> Self {
|
||||
#[unsafe(no_mangle)]
|
||||
static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![cfg_attr(not(feature = "rt"), expect(unused))]
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
use portable_atomic::{AtomicU8, Ordering};
|
||||
@ -142,6 +144,7 @@ pub(crate) fn addr_in_range(addr: usize, range: Range<usize>) -> bool {
|
||||
range.contains(&addr)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(riscv)]
|
||||
#[unsafe(export_name = "hal_main")]
|
||||
fn hal_main(a0: usize, a1: usize, a2: usize) -> ! {
|
||||
@ -158,6 +161,7 @@ fn hal_main(a0: usize, a1: usize, a2: usize) -> ! {
|
||||
}
|
||||
|
||||
#[cfg(xtensa)]
|
||||
#[cfg(feature = "rt")]
|
||||
#[unsafe(no_mangle)]
|
||||
#[cfg_attr(esp32s3, unsafe(link_section = ".rwtext"))]
|
||||
unsafe extern "C" fn ESP32Reset() -> ! {
|
||||
@ -234,11 +238,13 @@ unsafe extern "C" fn ESP32Reset() -> ! {
|
||||
unsafe { xtensa_lx_rt::Reset() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[unsafe(export_name = "__stack_chk_fail")]
|
||||
unsafe extern "C" fn stack_chk_fail() {
|
||||
panic!("Stack corruption detected");
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
fn setup_stack_guard() {
|
||||
unsafe extern "C" {
|
||||
static mut __stack_chk_guard: u32;
|
||||
|
@ -146,7 +146,7 @@ mod single_core {
|
||||
if #[cfg(riscv)] {
|
||||
if token != 0 {
|
||||
unsafe {
|
||||
esp_riscv_rt::riscv::interrupt::enable();
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
} else if #[cfg(xtensa)] {
|
||||
|
@ -226,6 +226,7 @@ static PERIPHERAL_REF_COUNT: Mutex<RefCell<[usize; Peripheral::COUNT]>> =
|
||||
/// Disable all peripherals.
|
||||
///
|
||||
/// Peripherals listed in [KEEP_ENABLED] are NOT disabled.
|
||||
#[cfg_attr(not(feature = "rt"), expect(dead_code))]
|
||||
pub(crate) fn disable_peripherals() {
|
||||
// Take the critical section up front to avoid taking it multiple times.
|
||||
critical_section::with(|cs| {
|
||||
|
@ -746,7 +746,7 @@ fn now() -> Instant {
|
||||
Instant::from_ticks(ticks / div)
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
#[cfg(all(esp32, feature = "rt"))]
|
||||
pub(crate) fn time_init() {
|
||||
let apb = crate::Clocks::get().apb_clock.as_hz();
|
||||
// we assume 80MHz APB clock source - there is no way to configure it in a
|
||||
|
@ -21,7 +21,7 @@ test = false
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
critical-section = "1.2.0"
|
||||
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal", features = ["requires-unstable"] }
|
||||
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal", default-features = false, features = ["requires-unstable"] }
|
||||
|
||||
# ⚠️ Unstable dependencies that are part of the public API
|
||||
heapless = "0.8.0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub use esp_hal::trapframe::TrapFrame;
|
||||
use esp_wifi_sys::c_types;
|
||||
|
||||
use super::*;
|
||||
pub use crate::hal::interrupt::TrapFrame;
|
||||
|
||||
pub(crate) fn new_task_context(
|
||||
task: extern "C" fn(*mut c_types::c_void),
|
||||
|
@ -179,6 +179,7 @@ impl Package {
|
||||
Package::EspConfig => features.push("build".to_owned()),
|
||||
Package::EspHal => {
|
||||
features.push("unstable".to_owned());
|
||||
features.push("rt".to_owned());
|
||||
if config.contains("psram") {
|
||||
// TODO this doesn't test octal psram (since `ESP_HAL_CONFIG_PSRAM_MODE`
|
||||
// defaults to `quad`) as it would require a separate build
|
||||
@ -193,6 +194,7 @@ impl Package {
|
||||
}
|
||||
Package::EspWifi => {
|
||||
features.push("esp-hal/unstable".to_owned());
|
||||
features.push("esp-hal/rt".to_owned());
|
||||
features.push("defmt".to_owned());
|
||||
if config.contains("wifi") {
|
||||
features.push("wifi".to_owned());
|
||||
@ -213,12 +215,14 @@ impl Package {
|
||||
}
|
||||
Package::EspHalEmbassy => {
|
||||
features.push("esp-hal/unstable".to_owned());
|
||||
features.push("esp-hal/rt".to_owned());
|
||||
features.push("defmt".to_owned());
|
||||
features.push("executors".to_owned());
|
||||
}
|
||||
Package::EspIeee802154 => {
|
||||
features.push("defmt".to_owned());
|
||||
features.push("esp-hal/unstable".to_owned());
|
||||
features.push("esp-hal/rt".to_owned());
|
||||
}
|
||||
Package::EspLpHal => {
|
||||
if config.contains("lp_core") {
|
||||
@ -251,12 +255,19 @@ impl Package {
|
||||
|
||||
match self {
|
||||
Package::EspHal => {
|
||||
// Make sure no additional features (e.g. no "unstable") still compiles:
|
||||
// This checks if the `esp-hal` crate compiles with the no features (other than the
|
||||
// chip selection)
|
||||
|
||||
// This tests that disabling the `rt` feature works
|
||||
cases.push(vec![]);
|
||||
// This checks if the `esp-hal` crate compiles _without_ the `unstable` feature
|
||||
// enabled
|
||||
cases.push(vec!["rt".to_owned()]);
|
||||
}
|
||||
Package::EspWifi => {
|
||||
// Minimal set of features that when enabled _should_ still compile:
|
||||
cases.push(vec![
|
||||
"esp-hal/rt".to_owned(),
|
||||
"esp-hal/unstable".to_owned(),
|
||||
"builtin-scheduler".to_owned(),
|
||||
]);
|
||||
|
@ -279,7 +279,9 @@ fn lint_package(
|
||||
builder = builder.arg(arg.to_string());
|
||||
}
|
||||
|
||||
builder = builder.arg(format!("--features={}", features.join(",")));
|
||||
if !features.is_empty() {
|
||||
builder = builder.arg(format!("--features={}", features.join(",")));
|
||||
}
|
||||
|
||||
let builder = if fix {
|
||||
builder.arg("--fix").arg("--lib").arg("--allow-dirty")
|
||||
|
Loading…
x
Reference in New Issue
Block a user