mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 21:00:59 +00:00
Simplify riscv trap handler (#3875)
* use `riscv::interrupt::nested` for nesting * Only save/restore caller saved registers in trap-frame (risc-v) * Replace RISC-V scheduler * Cleanup * Clippy * CHANGELOG.md * Make sure to reset the runlevel to current, not prio-1 * Clippy * Fix it for real * Address review comments * Address review comments * More docs, optimize away one instruction * Address review comments * Address review comments * Use a bool for the nested flag again * Fix * Clippy
This commit is contained in:
parent
3c21662606
commit
dfd66be8ab
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- A reimplemntation of the `assign_resources!` macro (#3809)
|
||||
- `TrngSource` to manage random number generator entropy (#3829)
|
||||
- On RISC-V you can opt-out of nested interrupts for an interrupt handler by using `new_not_nested` (#3875)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -135,19 +135,50 @@ pub trait InterruptConfigurable: crate::private::Sealed {
|
||||
pub struct InterruptHandler {
|
||||
f: extern "C" fn(),
|
||||
prio: Priority,
|
||||
#[cfg(riscv)]
|
||||
nested: bool,
|
||||
}
|
||||
|
||||
impl InterruptHandler {
|
||||
/// Creates a new [InterruptHandler] which will call the given function at
|
||||
/// the given priority.
|
||||
pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
|
||||
Self { f, prio }
|
||||
Self {
|
||||
f,
|
||||
prio,
|
||||
#[cfg(riscv)]
|
||||
nested: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// The function to be called
|
||||
/// Creates a new [InterruptHandler] which will call the given function at
|
||||
/// the given priority with disabled interrupt nesting.
|
||||
///
|
||||
/// Usually higher priority interrupts get served while handling an interrupt.
|
||||
/// Using this the interrupt handler won't get preempted by higher priority interrupts.
|
||||
#[cfg(riscv)]
|
||||
pub fn new_not_nested(f: extern "C" fn(), prio: Priority) -> Self {
|
||||
Self {
|
||||
f,
|
||||
prio,
|
||||
nested: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// The function to be called.
|
||||
#[cfg_attr(
|
||||
riscv,
|
||||
doc = "\n\nNote that the function pointer might be misaligned. Don't ever just call the function."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn handler(&self) -> extern "C" fn() {
|
||||
self.f
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(riscv)] {
|
||||
unsafe { core::mem::transmute::<usize, extern "C" fn()>((self.f as usize) | !self.nested as usize) }
|
||||
} else {
|
||||
self.f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Priority to be used when registering the interrupt
|
||||
|
@ -542,45 +542,6 @@ mod classic {
|
||||
|
||||
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 = intr.cpu_int_pri(interrupt_id).read().bits();
|
||||
|
||||
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)]
|
||||
@ -723,53 +684,6 @@ mod plic {
|
||||
|
||||
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")]
|
||||
@ -849,12 +763,25 @@ mod rt {
|
||||
let configured_interrupts = vectored::configured_interrupts(core, status, prio);
|
||||
|
||||
for interrupt_nr in configured_interrupts.iterator() {
|
||||
let handler = unsafe { pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler };
|
||||
let handler =
|
||||
unsafe { pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler } as usize;
|
||||
let not_nested = (handler & 1) == 1;
|
||||
let handler = handler & !1;
|
||||
|
||||
let handler: fn(&mut TrapFrame) = unsafe {
|
||||
core::mem::transmute::<unsafe extern "C" fn(), fn(&mut TrapFrame)>(handler)
|
||||
};
|
||||
handler(context);
|
||||
let handler: fn(&mut TrapFrame) =
|
||||
unsafe { core::mem::transmute::<usize, fn(&mut TrapFrame)>(handler) };
|
||||
|
||||
if not_nested || prio == Priority::Priority15 {
|
||||
handler(context);
|
||||
} else {
|
||||
let elevated = prio as u8;
|
||||
unsafe {
|
||||
let level =
|
||||
change_current_runlevel(unwrap!(Priority::try_from(elevated as u32)));
|
||||
riscv::interrupt::nested(|| handler(context));
|
||||
change_current_runlevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ document-features = "0.2.11"
|
||||
esp-alloc = { version = "0.8.0", path = "../esp-alloc", optional = true }
|
||||
esp-config = { version = "0.5.0", path = "../esp-config" }
|
||||
esp-radio-preempt-driver = { version = "0.0.1", path = "../esp-radio-preempt-driver" }
|
||||
portable-atomic = { version = "1.11.0", default-features = false }
|
||||
|
||||
# Logging interfaces, they are mutually exclusive so they need to be behind separate features.
|
||||
defmt = { version = "1.0", optional = true }
|
||||
|
@ -153,6 +153,7 @@ impl SchedulerState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(xtensa)]
|
||||
fn switch_task(&mut self, trap_frame: &mut TrapFrame) {
|
||||
task::save_task_context(unsafe { &mut *self.current_task }, trap_frame);
|
||||
|
||||
@ -166,6 +167,26 @@ impl SchedulerState {
|
||||
task::restore_task_context(unsafe { &mut *self.current_task }, trap_frame);
|
||||
}
|
||||
|
||||
#[cfg(riscv)]
|
||||
fn switch_task(&mut self, _trap_frame: &mut TrapFrame) {
|
||||
if !self.to_delete.is_null() {
|
||||
let task_to_delete = core::mem::take(&mut self.to_delete);
|
||||
self.delete_task(task_to_delete);
|
||||
}
|
||||
|
||||
let task = self.current_task;
|
||||
let context = unsafe { &mut (*task).trap_frame };
|
||||
let old_ctx = core::ptr::addr_of_mut!(*context);
|
||||
|
||||
let task = unsafe { (*self.current_task).next };
|
||||
let context = unsafe { &mut (*task).trap_frame };
|
||||
let new_ctx = core::ptr::addr_of_mut!(*context);
|
||||
|
||||
if crate::task::arch_specific::task_switch(old_ctx, new_ctx) {
|
||||
unsafe { self.current_task = (*self.current_task).next };
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_task_deletion(&mut self, task: *mut Context) -> bool {
|
||||
if task.is_null() {
|
||||
self.to_delete = self.current_task;
|
||||
|
@ -1,17 +1,23 @@
|
||||
#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")]
|
||||
#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
|
||||
mod arch_specific;
|
||||
#[cfg_attr(riscv, path = "riscv.rs")]
|
||||
#[cfg_attr(xtensa, path = "xtensa.rs")]
|
||||
pub(crate) mod arch_specific;
|
||||
|
||||
use core::{ffi::c_void, mem::MaybeUninit};
|
||||
|
||||
use allocator_api2::boxed::Box;
|
||||
#[cfg(riscv)]
|
||||
use arch_specific::Registers;
|
||||
pub(crate) use arch_specific::*;
|
||||
use esp_hal::trapframe::TrapFrame;
|
||||
|
||||
use crate::{InternalMemory, SCHEDULER_STATE, task, timer};
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct Context {
|
||||
trap_frame: TrapFrame,
|
||||
#[cfg(riscv)]
|
||||
pub trap_frame: Registers,
|
||||
#[cfg(xtensa)]
|
||||
pub trap_frame: TrapFrame,
|
||||
pub thread_semaphore: u32,
|
||||
pub next: *mut Context,
|
||||
pub _allocated_stack: Box<[MaybeUninit<u8>], InternalMemory>,
|
||||
@ -42,6 +48,9 @@ pub(super) fn allocate_main_task() {
|
||||
// This context will be filled out by the first context switch.
|
||||
let context = Box::new_in(
|
||||
Context {
|
||||
#[cfg(riscv)]
|
||||
trap_frame: Registers::default(),
|
||||
#[cfg(xtensa)]
|
||||
trap_frame: TrapFrame::default(),
|
||||
thread_semaphore: 0,
|
||||
next: core::ptr::null_mut(),
|
||||
|
@ -1,18 +1,108 @@
|
||||
use core::ffi::c_void;
|
||||
|
||||
pub use esp_hal::trapframe::TrapFrame;
|
||||
unsafe extern "C" {
|
||||
fn sys_switch();
|
||||
}
|
||||
|
||||
use crate::task::Context;
|
||||
static _CURRENT_CTX_PTR: portable_atomic::AtomicPtr<Registers> =
|
||||
portable_atomic::AtomicPtr::new(core::ptr::null_mut());
|
||||
|
||||
static _NEXT_CTX_PTR: portable_atomic::AtomicPtr<Registers> =
|
||||
portable_atomic::AtomicPtr::new(core::ptr::null_mut());
|
||||
|
||||
/// Registers saved / restored
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct Registers {
|
||||
/// Return address, stores the address to return to after a function call or
|
||||
/// interrupt.
|
||||
pub ra: usize,
|
||||
/// Temporary register t0, used for intermediate values.
|
||||
pub t0: usize,
|
||||
/// Temporary register t1, used for intermediate values.
|
||||
pub t1: usize,
|
||||
/// Temporary register t2, used for intermediate values.
|
||||
pub t2: usize,
|
||||
/// Temporary register t3, used for intermediate values.
|
||||
pub t3: usize,
|
||||
/// Temporary register t4, used for intermediate values.
|
||||
pub t4: usize,
|
||||
/// Temporary register t5, used for intermediate values.
|
||||
pub t5: usize,
|
||||
/// Temporary register t6, used for intermediate values.
|
||||
pub t6: usize,
|
||||
/// Argument register a0, typically used to pass the first argument to a
|
||||
/// function.
|
||||
pub a0: usize,
|
||||
/// Argument register a1, typically used to pass the second argument to a
|
||||
/// function.
|
||||
pub a1: usize,
|
||||
/// Argument register a2, typically used to pass the third argument to a
|
||||
/// function.
|
||||
pub a2: usize,
|
||||
/// Argument register a3, typically used to pass the fourth argument to a
|
||||
/// function.
|
||||
pub a3: usize,
|
||||
/// Argument register a4, typically used to pass the fifth argument to a
|
||||
/// function.
|
||||
pub a4: usize,
|
||||
/// Argument register a5, typically used to pass the sixth argument to a
|
||||
/// function.
|
||||
pub a5: usize,
|
||||
/// Argument register a6, typically used to pass the seventh argument to a
|
||||
/// function.
|
||||
pub a6: usize,
|
||||
/// Argument register a7, typically used to pass the eighth argument to a
|
||||
/// function.
|
||||
pub a7: usize,
|
||||
/// Saved register s0, used to hold values across function calls.
|
||||
pub s0: usize,
|
||||
/// Saved register s1, used to hold values across function calls.
|
||||
pub s1: usize,
|
||||
/// Saved register s2, used to hold values across function calls.
|
||||
pub s2: usize,
|
||||
/// Saved register s3, used to hold values across function calls.
|
||||
pub s3: usize,
|
||||
/// Saved register s4, used to hold values across function calls.
|
||||
pub s4: usize,
|
||||
/// Saved register s5, used to hold values across function calls.
|
||||
pub s5: usize,
|
||||
/// Saved register s6, used to hold values across function calls.
|
||||
pub s6: usize,
|
||||
/// Saved register s7, used to hold values across function calls.
|
||||
pub s7: usize,
|
||||
/// Saved register s8, used to hold values across function calls.
|
||||
pub s8: usize,
|
||||
/// Saved register s9, used to hold values across function calls.
|
||||
pub s9: usize,
|
||||
/// Saved register s10, used to hold values across function calls.
|
||||
pub s10: usize,
|
||||
/// Saved register s11, used to hold values across function calls.
|
||||
pub s11: usize,
|
||||
/// Global pointer register, holds the address of the global data area.
|
||||
pub gp: usize,
|
||||
/// Thread pointer register, holds the address of the thread-local storage
|
||||
/// area.
|
||||
pub tp: usize,
|
||||
/// Stack pointer register, holds the address of the top of the stack.
|
||||
pub sp: usize,
|
||||
/// Program counter, stores the address of the next instruction to be
|
||||
/// executed.
|
||||
pub pc: usize,
|
||||
|
||||
/// The mstatus which will be loaded before MRET
|
||||
pub mstatus: usize,
|
||||
}
|
||||
|
||||
pub(crate) fn new_task_context(
|
||||
task: extern "C" fn(*mut c_void),
|
||||
param: *mut c_void,
|
||||
stack_top: *mut (),
|
||||
) -> TrapFrame {
|
||||
) -> Registers {
|
||||
let stack_top = stack_top as usize;
|
||||
let stack_top = stack_top - (stack_top % 16);
|
||||
|
||||
TrapFrame {
|
||||
Registers {
|
||||
pc: task as usize,
|
||||
a0: param as usize,
|
||||
sp: stack_top,
|
||||
@ -20,72 +110,171 @@ pub(crate) fn new_task_context(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn restore_task_context(ctx: &mut Context, trap_frame: &mut TrapFrame) {
|
||||
trap_frame.ra = ctx.trap_frame.ra;
|
||||
trap_frame.sp = ctx.trap_frame.sp;
|
||||
trap_frame.a0 = ctx.trap_frame.a0;
|
||||
trap_frame.a1 = ctx.trap_frame.a1;
|
||||
trap_frame.a2 = ctx.trap_frame.a2;
|
||||
trap_frame.a3 = ctx.trap_frame.a3;
|
||||
trap_frame.a4 = ctx.trap_frame.a4;
|
||||
trap_frame.a5 = ctx.trap_frame.a5;
|
||||
trap_frame.a6 = ctx.trap_frame.a6;
|
||||
trap_frame.a7 = ctx.trap_frame.a7;
|
||||
trap_frame.t0 = ctx.trap_frame.t0;
|
||||
trap_frame.t1 = ctx.trap_frame.t1;
|
||||
trap_frame.t2 = ctx.trap_frame.t2;
|
||||
trap_frame.t3 = ctx.trap_frame.t3;
|
||||
trap_frame.t4 = ctx.trap_frame.t4;
|
||||
trap_frame.t5 = ctx.trap_frame.t5;
|
||||
trap_frame.t6 = ctx.trap_frame.t6;
|
||||
trap_frame.s0 = ctx.trap_frame.s0;
|
||||
trap_frame.s1 = ctx.trap_frame.s1;
|
||||
trap_frame.s2 = ctx.trap_frame.s2;
|
||||
trap_frame.s3 = ctx.trap_frame.s3;
|
||||
trap_frame.s4 = ctx.trap_frame.s4;
|
||||
trap_frame.s5 = ctx.trap_frame.s5;
|
||||
trap_frame.s6 = ctx.trap_frame.s6;
|
||||
trap_frame.s7 = ctx.trap_frame.s7;
|
||||
trap_frame.s8 = ctx.trap_frame.s8;
|
||||
trap_frame.s9 = ctx.trap_frame.s9;
|
||||
trap_frame.s10 = ctx.trap_frame.s10;
|
||||
trap_frame.s11 = ctx.trap_frame.s11;
|
||||
trap_frame.gp = ctx.trap_frame.gp;
|
||||
trap_frame.tp = ctx.trap_frame.tp;
|
||||
trap_frame.pc = ctx.trap_frame.pc;
|
||||
/// Switch to next task
|
||||
///
|
||||
/// *MUST* be called from inside an ISR without interrupt nesting.
|
||||
///
|
||||
/// The task switching relies on MEPC and MSTATUS to not get clobbered.
|
||||
/// We save MEPC as the current task's PC and change MEPC to an assembly function
|
||||
/// which will save the current CPU state for the current task (excluding PC) and
|
||||
/// restoring the CPU state from the next task.
|
||||
pub fn task_switch(old_ctx: *mut Registers, new_ctx: *mut Registers) -> bool {
|
||||
// Check that there isn't a switch "in-progress"
|
||||
//
|
||||
// While this shouldn't happen: from observation it does!
|
||||
//
|
||||
// This happens if the timer tick interrupt gets asserted while the task switch is in
|
||||
// progress (i.e. the sw-int is served).
|
||||
//
|
||||
// In that case returning via `mret` will first service the pending interrupt before actually
|
||||
// ending up in `sys_switch`.
|
||||
//
|
||||
// Setting MPIE to 0 _should_ prevent that from happening.
|
||||
if !_NEXT_CTX_PTR
|
||||
.load(portable_atomic::Ordering::SeqCst)
|
||||
.is_null()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_CURRENT_CTX_PTR.store(old_ctx, portable_atomic::Ordering::SeqCst);
|
||||
_NEXT_CTX_PTR.store(new_ctx, portable_atomic::Ordering::SeqCst);
|
||||
|
||||
let old = esp_hal::riscv::register::mepc::read();
|
||||
unsafe {
|
||||
(*old_ctx).pc = old;
|
||||
}
|
||||
|
||||
// set MSTATUS for the switched to task
|
||||
// MIE will be set from MPIE
|
||||
// MPP will be used to determine the privilege-level
|
||||
let mstatus = esp_hal::riscv::register::mstatus::read().bits();
|
||||
unsafe {
|
||||
(*new_ctx).mstatus = mstatus;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// set MPIE in MSTATUS to 0 to disable interrupts while task switching
|
||||
esp_hal::riscv::register::mstatus::write(
|
||||
esp_hal::riscv::register::mstatus::Mstatus::from_bits(mstatus & !(1 << 7)),
|
||||
);
|
||||
|
||||
// load address of sys_switch into MEPC - will run after all registers are restored
|
||||
esp_hal::riscv::register::mepc::write(sys_switch as usize);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn save_task_context(ctx: &mut Context, trap_frame: &TrapFrame) {
|
||||
ctx.trap_frame.ra = trap_frame.ra;
|
||||
ctx.trap_frame.sp = trap_frame.sp;
|
||||
ctx.trap_frame.a0 = trap_frame.a0;
|
||||
ctx.trap_frame.a1 = trap_frame.a1;
|
||||
ctx.trap_frame.a2 = trap_frame.a2;
|
||||
ctx.trap_frame.a3 = trap_frame.a3;
|
||||
ctx.trap_frame.a4 = trap_frame.a4;
|
||||
ctx.trap_frame.a5 = trap_frame.a5;
|
||||
ctx.trap_frame.a6 = trap_frame.a6;
|
||||
ctx.trap_frame.a7 = trap_frame.a7;
|
||||
ctx.trap_frame.t0 = trap_frame.t0;
|
||||
ctx.trap_frame.t1 = trap_frame.t1;
|
||||
ctx.trap_frame.t2 = trap_frame.t2;
|
||||
ctx.trap_frame.t3 = trap_frame.t3;
|
||||
ctx.trap_frame.t4 = trap_frame.t4;
|
||||
ctx.trap_frame.t5 = trap_frame.t5;
|
||||
ctx.trap_frame.t6 = trap_frame.t6;
|
||||
ctx.trap_frame.s0 = trap_frame.s0;
|
||||
ctx.trap_frame.s1 = trap_frame.s1;
|
||||
ctx.trap_frame.s2 = trap_frame.s2;
|
||||
ctx.trap_frame.s3 = trap_frame.s3;
|
||||
ctx.trap_frame.s4 = trap_frame.s4;
|
||||
ctx.trap_frame.s5 = trap_frame.s5;
|
||||
ctx.trap_frame.s6 = trap_frame.s6;
|
||||
ctx.trap_frame.s7 = trap_frame.s7;
|
||||
ctx.trap_frame.s8 = trap_frame.s8;
|
||||
ctx.trap_frame.s9 = trap_frame.s9;
|
||||
ctx.trap_frame.s10 = trap_frame.s10;
|
||||
ctx.trap_frame.s11 = trap_frame.s11;
|
||||
ctx.trap_frame.gp = trap_frame.gp;
|
||||
ctx.trap_frame.tp = trap_frame.tp;
|
||||
ctx.trap_frame.pc = trap_frame.pc;
|
||||
}
|
||||
core::arch::global_asm!(
|
||||
r#"
|
||||
.section .trap, "ax"
|
||||
|
||||
.globl sys_switch
|
||||
.align 4
|
||||
sys_switch:
|
||||
# put some regs on the stack since we will need those regs
|
||||
addi sp, sp, -16
|
||||
sw t0, 0(sp)
|
||||
sw t1, 4(sp)
|
||||
|
||||
# t0 => current context
|
||||
la t0, {_CURRENT_CTX_PTR}
|
||||
lw t0, 0(t0)
|
||||
|
||||
# store registers to old context - PC needs to be set by the "caller"
|
||||
sw ra, 0*4(t0)
|
||||
|
||||
lw t1, 0(sp)
|
||||
sw t1, 1*4(t0)
|
||||
|
||||
lw t1, 4(sp)
|
||||
sw t1, 2*4(t0)
|
||||
|
||||
sw t2, 3*4(t0)
|
||||
sw t3, 4*4(t0)
|
||||
sw t4, 5*4(t0)
|
||||
sw t5, 6*4(t0)
|
||||
sw t6, 7*4(t0)
|
||||
sw a0, 8*4(t0)
|
||||
sw a1, 9*4(t0)
|
||||
sw a2, 10*4(t0)
|
||||
sw a3, 11*4(t0)
|
||||
sw a4, 12*4(t0)
|
||||
sw a5, 13*4(t0)
|
||||
sw a6, 14*4(t0)
|
||||
sw a7, 15*4(t0)
|
||||
sw s0, 16*4(t0)
|
||||
sw s1, 17*4(t0)
|
||||
sw s2, 18*4(t0)
|
||||
sw s3, 19*4(t0)
|
||||
sw s4, 20*4(t0)
|
||||
sw s5, 21*4(t0)
|
||||
sw s6, 22*4(t0)
|
||||
sw s7, 23*4(t0)
|
||||
sw s8, 24*4(t0)
|
||||
sw s9, 25*4(t0)
|
||||
sw s10, 26*4(t0)
|
||||
sw s11, 27*4(t0)
|
||||
sw gp, 28*4(t0)
|
||||
sw tp, 29*4(t0)
|
||||
|
||||
addi t1, sp, 16
|
||||
sw t1, 30*4(t0)
|
||||
|
||||
# t0 => next context
|
||||
la t1, {_NEXT_CTX_PTR}
|
||||
lw t0, 0(t1)
|
||||
|
||||
# signal that the task switch is done - safe to do it already now - interrupts are disabled
|
||||
sw x0, 0(t1)
|
||||
|
||||
# set the next task's PC as MEPC
|
||||
lw t1, 31*4(t0)
|
||||
csrrw x0, mepc, t1
|
||||
|
||||
# set MSTATUS from next context
|
||||
lw t1, 32*4(t0)
|
||||
csrrw x0, mstatus, t1
|
||||
|
||||
# restore registers from old context
|
||||
lw ra, 0*4(t0)
|
||||
lw t1, 2*4(t0)
|
||||
lw t2, 3*4(t0)
|
||||
lw t3, 4*4(t0)
|
||||
lw t4, 5*4(t0)
|
||||
lw t5, 6*4(t0)
|
||||
lw t6, 7*4(t0)
|
||||
lw a0, 8*4(t0)
|
||||
lw a1, 9*4(t0)
|
||||
lw a2, 10*4(t0)
|
||||
lw a3, 11*4(t0)
|
||||
lw a4, 12*4(t0)
|
||||
lw a5, 13*4(t0)
|
||||
lw a6, 14*4(t0)
|
||||
lw a7, 15*4(t0)
|
||||
lw s0, 16*4(t0)
|
||||
lw s1, 17*4(t0)
|
||||
lw s2, 18*4(t0)
|
||||
lw s3, 19*4(t0)
|
||||
lw s4, 20*4(t0)
|
||||
lw s5, 21*4(t0)
|
||||
lw s6, 22*4(t0)
|
||||
lw s7, 23*4(t0)
|
||||
lw s8, 24*4(t0)
|
||||
lw s9, 25*4(t0)
|
||||
lw s10, 26*4(t0)
|
||||
lw s11, 27*4(t0)
|
||||
lw gp, 28*4(t0)
|
||||
lw tp, 29*4(t0)
|
||||
lw sp, 30*4(t0)
|
||||
lw t0, 1*4(t0)
|
||||
|
||||
|
||||
# jump to next task's PC
|
||||
mret
|
||||
|
||||
"#,
|
||||
_CURRENT_CTX_PTR = sym _CURRENT_CTX_PTR,
|
||||
_NEXT_CTX_PTR = sym _NEXT_CTX_PTR,
|
||||
);
|
||||
|
@ -1,12 +1,11 @@
|
||||
#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")]
|
||||
#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
|
||||
#[cfg_attr(riscv, path = "riscv.rs")]
|
||||
#[cfg_attr(xtensa, path = "xtensa.rs")]
|
||||
mod arch_specific;
|
||||
|
||||
pub(crate) use arch_specific::*;
|
||||
use esp_hal::{
|
||||
interrupt::{InterruptHandler, Priority},
|
||||
sync::Locked,
|
||||
trapframe::TrapFrame,
|
||||
};
|
||||
|
||||
use crate::TimeBase;
|
||||
@ -22,6 +21,13 @@ pub(crate) fn setup_timebase(mut timer: TimeBase) {
|
||||
// The timer needs to tick at Priority 1 to prevent accidentally interrupting
|
||||
// priority 1 limited locks.
|
||||
let cb: extern "C" fn() = unsafe { core::mem::transmute(timer_tick_handler as *const ()) };
|
||||
|
||||
// Register the interrupt handler without nesting to satisfy the requirements of the task
|
||||
// switching code
|
||||
#[cfg(riscv)]
|
||||
let handler = InterruptHandler::new_not_nested(cb, Priority::Priority1);
|
||||
|
||||
#[cfg(xtensa)]
|
||||
let handler = InterruptHandler::new(cb, Priority::Priority1);
|
||||
|
||||
timer.set_interrupt_handler(handler);
|
||||
@ -44,18 +50,3 @@ pub(crate) fn disable_timebase() {
|
||||
unwrap!(timer.cancel());
|
||||
});
|
||||
}
|
||||
|
||||
#[esp_hal::ram]
|
||||
extern "C" fn timer_tick_handler(context: &mut TrapFrame) {
|
||||
clear_timer_interrupt();
|
||||
|
||||
// `task_switch` must be called on a single interrupt priority level only.
|
||||
// Because on ESP32 the software interrupt is triggered at priority 3 but
|
||||
// the timer interrupt is triggered at priority 1, we need to trigger the
|
||||
// software interrupt manually.
|
||||
if cfg!(esp32) {
|
||||
yield_task();
|
||||
} else {
|
||||
crate::task::task_switch(context);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,27 @@
|
||||
use esp_hal::{
|
||||
interrupt::{self, TrapFrame, software::SoftwareInterrupt},
|
||||
interrupt::{self, software::SoftwareInterrupt},
|
||||
peripherals::Interrupt,
|
||||
};
|
||||
|
||||
use crate::task::task_switch;
|
||||
|
||||
pub(crate) fn setup_multitasking() {
|
||||
unwrap!(interrupt::enable(
|
||||
Interrupt::FROM_CPU_INTR2,
|
||||
interrupt::Priority::Priority1,
|
||||
));
|
||||
// Register the interrupt handler without nesting to satisfy the requirements of the task
|
||||
// switching code
|
||||
let swint2_handler = esp_hal::interrupt::InterruptHandler::new_not_nested(
|
||||
unsafe { core::mem::transmute::<*const (), extern "C" fn()>(swint2_handler as *const ()) },
|
||||
esp_hal::interrupt::Priority::Priority1,
|
||||
);
|
||||
|
||||
unsafe { SoftwareInterrupt::<2>::steal() }.set_interrupt_handler(swint2_handler);
|
||||
}
|
||||
|
||||
pub(crate) fn disable_multitasking() {
|
||||
interrupt::disable(esp_hal::system::Cpu::ProCpu, Interrupt::FROM_CPU_INTR2);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[esp_hal::ram]
|
||||
extern "C" fn FROM_CPU_INTR2(trap_frame: &mut TrapFrame) {
|
||||
extern "C" fn swint2_handler(trap_frame: &mut esp_hal::interrupt::TrapFrame) {
|
||||
// clear FROM_CPU_INTR2
|
||||
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
||||
swi.reset();
|
||||
@ -32,3 +35,9 @@ pub(crate) fn yield_task() {
|
||||
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
||||
swi.raise();
|
||||
}
|
||||
|
||||
#[esp_hal::ram]
|
||||
pub(crate) extern "C" fn timer_tick_handler(trap_frame: &mut esp_hal::interrupt::TrapFrame) {
|
||||
super::clear_timer_interrupt();
|
||||
crate::task::task_switch(trap_frame);
|
||||
}
|
||||
|
@ -38,3 +38,18 @@ pub(crate) fn yield_task() {
|
||||
let intr = SW_INTERRUPT;
|
||||
unsafe { core::arch::asm!("wsr.intset {0}", in(reg) intr, options(nostack)) };
|
||||
}
|
||||
|
||||
#[esp_hal::ram]
|
||||
pub(crate) extern "C" fn timer_tick_handler(_context: &mut TrapFrame) {
|
||||
super::clear_timer_interrupt();
|
||||
|
||||
// `task_switch` must be called on a single interrupt priority level only.
|
||||
// Because on ESP32 the software interrupt is triggered at priority 3 but
|
||||
// the timer interrupt is triggered at priority 1, we need to trigger the
|
||||
// software interrupt manually.
|
||||
if cfg!(esp32) {
|
||||
yield_task();
|
||||
} else {
|
||||
crate::task::task_switch(_context);
|
||||
}
|
||||
}
|
||||
|
@ -113,49 +113,6 @@ pub struct TrapFrame {
|
||||
/// Argument register a7, typically used to pass the eighth argument to a
|
||||
/// function.
|
||||
pub a7: usize,
|
||||
/// Saved register s0, used to hold values across function calls.
|
||||
pub s0: usize,
|
||||
/// Saved register s1, used to hold values across function calls.
|
||||
pub s1: usize,
|
||||
/// Saved register s2, used to hold values across function calls.
|
||||
pub s2: usize,
|
||||
/// Saved register s3, used to hold values across function calls.
|
||||
pub s3: usize,
|
||||
/// Saved register s4, used to hold values across function calls.
|
||||
pub s4: usize,
|
||||
/// Saved register s5, used to hold values across function calls.
|
||||
pub s5: usize,
|
||||
/// Saved register s6, used to hold values across function calls.
|
||||
pub s6: usize,
|
||||
/// Saved register s7, used to hold values across function calls.
|
||||
pub s7: usize,
|
||||
/// Saved register s8, used to hold values across function calls.
|
||||
pub s8: usize,
|
||||
/// Saved register s9, used to hold values across function calls.
|
||||
pub s9: usize,
|
||||
/// Saved register s10, used to hold values across function calls.
|
||||
pub s10: usize,
|
||||
/// Saved register s11, used to hold values across function calls.
|
||||
pub s11: usize,
|
||||
/// Global pointer register, holds the address of the global data area.
|
||||
pub gp: usize,
|
||||
/// Thread pointer register, holds the address of the thread-local storage
|
||||
/// area.
|
||||
pub tp: usize,
|
||||
/// Stack pointer register, holds the address of the top of the stack.
|
||||
pub sp: usize,
|
||||
/// Program counter, stores the address of the next instruction to be
|
||||
/// executed.
|
||||
pub pc: usize,
|
||||
/// Machine status register, holds the current status of the processor,
|
||||
/// including interrupt enable bits and privilege mode.
|
||||
pub mstatus: usize,
|
||||
/// Machine cause register, contains the reason for the trap (e.g.,
|
||||
/// exception or interrupt number).
|
||||
pub mcause: usize,
|
||||
/// Machine trap value register, contains additional information about the
|
||||
/// trap (e.g., faulting address).
|
||||
pub mtval: usize,
|
||||
}
|
||||
|
||||
/// Trap entry point rust (_start_trap_rust)
|
||||
@ -514,162 +471,162 @@ _start_trap: // Handle exceptions in vectored mode
|
||||
csrr t0, mscratch
|
||||
// now SP is in RAM - continue
|
||||
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, _start_trap_rust_hal /* Load the HAL trap handler */
|
||||
j _start_trap_direct
|
||||
_start_trap1:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt1
|
||||
j _start_trap_direct
|
||||
_start_trap2:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt2
|
||||
j _start_trap_direct
|
||||
_start_trap3:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt3
|
||||
j _start_trap_direct
|
||||
_start_trap4:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt4
|
||||
j _start_trap_direct
|
||||
_start_trap5:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt5
|
||||
j _start_trap_direct
|
||||
_start_trap6:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt6
|
||||
j _start_trap_direct
|
||||
_start_trap7:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt7
|
||||
j _start_trap_direct
|
||||
_start_trap8:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt8
|
||||
j _start_trap_direct
|
||||
_start_trap9:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt9
|
||||
j _start_trap_direct
|
||||
_start_trap10:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt10
|
||||
j _start_trap_direct
|
||||
_start_trap11:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt11
|
||||
j _start_trap_direct
|
||||
_start_trap12:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt12
|
||||
j _start_trap_direct
|
||||
_start_trap13:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt13
|
||||
j _start_trap_direct
|
||||
_start_trap14:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt14
|
||||
j _start_trap_direct
|
||||
_start_trap15:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt15
|
||||
j _start_trap_direct
|
||||
_start_trap16:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt16
|
||||
j _start_trap_direct
|
||||
_start_trap17:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt17
|
||||
j _start_trap_direct
|
||||
_start_trap18:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt18
|
||||
j _start_trap_direct
|
||||
_start_trap19:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt19
|
||||
j _start_trap_direct
|
||||
_start_trap20:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt20
|
||||
j _start_trap_direct
|
||||
_start_trap21:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt21
|
||||
j _start_trap_direct
|
||||
_start_trap22:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt22
|
||||
j _start_trap_direct
|
||||
_start_trap23:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt23
|
||||
j _start_trap_direct
|
||||
_start_trap24:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt24
|
||||
j _start_trap_direct
|
||||
_start_trap25:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt25
|
||||
j _start_trap_direct
|
||||
_start_trap26:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt26
|
||||
j _start_trap_direct
|
||||
_start_trap27:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt27
|
||||
j _start_trap_direct
|
||||
_start_trap28:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt28
|
||||
j _start_trap_direct
|
||||
_start_trap29:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt29
|
||||
j _start_trap_direct
|
||||
_start_trap30:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt30
|
||||
j _start_trap_direct
|
||||
_start_trap31:
|
||||
addi sp, sp, -40*4
|
||||
addi sp, sp, -16*4
|
||||
sw ra, 0(sp)
|
||||
la ra, interrupt31
|
||||
j _start_trap_direct
|
||||
@ -692,59 +649,10 @@ r#"
|
||||
sw a5, 13*4(sp)
|
||||
sw a6, 14*4(sp)
|
||||
sw a7, 15*4(sp)
|
||||
sw s0, 16*4(sp)
|
||||
sw s1, 17*4(sp)
|
||||
sw s2, 18*4(sp)
|
||||
sw s3, 19*4(sp)
|
||||
sw s4, 20*4(sp)
|
||||
sw s5, 21*4(sp)
|
||||
sw s6, 22*4(sp)
|
||||
sw s7, 23*4(sp)
|
||||
sw s8, 24*4(sp)
|
||||
sw s9, 25*4(sp)
|
||||
sw s10, 26*4(sp)
|
||||
sw s11, 27*4(sp)
|
||||
sw gp, 28*4(sp)
|
||||
sw tp, 29*4(sp)
|
||||
csrrs t1, mepc, x0
|
||||
sw t1, 31*4(sp)
|
||||
csrrs t1, mstatus, x0
|
||||
sw t1, 32*4(sp)
|
||||
csrrs t1, mcause, x0
|
||||
sw t1, 33*4(sp)
|
||||
csrrs t1, mtval, x0
|
||||
sw t1, 34*4(sp)
|
||||
|
||||
addi s0, sp, 40*4
|
||||
sw s0, 30*4(sp)
|
||||
|
||||
add a0, sp, zero
|
||||
"#,
|
||||
// store current priority, set threshold, enable interrupts
|
||||
r#"
|
||||
addi sp, sp, -16 #build stack
|
||||
sw ra, 0(sp)
|
||||
jal ra, _handle_priority
|
||||
lw ra, 0(sp)
|
||||
sw a0, 0(sp) #reuse old stack, a0 is return of _handle_priority
|
||||
addi a0, sp, 16 #the proper stack pointer is an argument to the HAL handler
|
||||
"#,
|
||||
// jump to handler loaded in direct handler
|
||||
r#"
|
||||
jalr ra, ra #jump to label loaded in _start_trapx
|
||||
"#,
|
||||
// restore threshold
|
||||
r#"
|
||||
lw a0, 0(sp) #load stored priority
|
||||
jal ra, _restore_priority
|
||||
addi sp, sp, 16 #pop
|
||||
"#,
|
||||
r#"
|
||||
lw t1, 31*4(sp)
|
||||
csrrw x0, mepc, t1
|
||||
|
||||
lw t1, 32*4(sp)
|
||||
csrrw x0, mstatus, t1
|
||||
# jump to handler loaded in direct handler
|
||||
add a0, sp, zero # load trap-frame address in a0
|
||||
jalr ra, ra # jump to label loaded in _start_trapX
|
||||
|
||||
lw ra, 0*4(sp)
|
||||
lw t0, 1*4(sp)
|
||||
@ -762,21 +670,8 @@ r#"
|
||||
lw a5, 13*4(sp)
|
||||
lw a6, 14*4(sp)
|
||||
lw a7, 15*4(sp)
|
||||
lw s0, 16*4(sp)
|
||||
lw s1, 17*4(sp)
|
||||
lw s2, 18*4(sp)
|
||||
lw s3, 19*4(sp)
|
||||
lw s4, 20*4(sp)
|
||||
lw s5, 21*4(sp)
|
||||
lw s6, 22*4(sp)
|
||||
lw s7, 23*4(sp)
|
||||
lw s8, 24*4(sp)
|
||||
lw s9, 25*4(sp)
|
||||
lw s10, 26*4(sp)
|
||||
lw s11, 27*4(sp)
|
||||
lw gp, 28*4(sp)
|
||||
lw tp, 29*4(sp)
|
||||
lw sp, 30*4(sp)
|
||||
|
||||
addi sp, sp, 16*4
|
||||
|
||||
# SP was restored from the original SP
|
||||
mret
|
||||
|
Loading…
x
Reference in New Issue
Block a user