mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-02 14:44:42 +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)
|
- A reimplemntation of the `assign_resources!` macro (#3809)
|
||||||
- `TrngSource` to manage random number generator entropy (#3829)
|
- `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
|
### Changed
|
||||||
|
|
||||||
|
@ -135,19 +135,50 @@ pub trait InterruptConfigurable: crate::private::Sealed {
|
|||||||
pub struct InterruptHandler {
|
pub struct InterruptHandler {
|
||||||
f: extern "C" fn(),
|
f: extern "C" fn(),
|
||||||
prio: Priority,
|
prio: Priority,
|
||||||
|
#[cfg(riscv)]
|
||||||
|
nested: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptHandler {
|
impl InterruptHandler {
|
||||||
/// Creates a new [InterruptHandler] which will call the given function at
|
/// Creates a new [InterruptHandler] which will call the given function at
|
||||||
/// the given priority.
|
/// the given priority.
|
||||||
pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
|
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]
|
#[inline]
|
||||||
pub fn handler(&self) -> extern "C" fn() {
|
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
|
/// Priority to be used when registering the interrupt
|
||||||
|
@ -542,45 +542,6 @@ mod classic {
|
|||||||
|
|
||||||
prev_interrupt_priority
|
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)]
|
#[cfg(plic)]
|
||||||
@ -723,53 +684,6 @@ mod plic {
|
|||||||
|
|
||||||
prev_interrupt_priority
|
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")]
|
#[cfg(feature = "rt")]
|
||||||
@ -849,12 +763,25 @@ mod rt {
|
|||||||
let configured_interrupts = vectored::configured_interrupts(core, status, prio);
|
let configured_interrupts = vectored::configured_interrupts(core, status, prio);
|
||||||
|
|
||||||
for interrupt_nr in configured_interrupts.iterator() {
|
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 {
|
let handler: fn(&mut TrapFrame) =
|
||||||
core::mem::transmute::<unsafe extern "C" fn(), fn(&mut TrapFrame)>(handler)
|
unsafe { core::mem::transmute::<usize, fn(&mut TrapFrame)>(handler) };
|
||||||
};
|
|
||||||
handler(context);
|
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-alloc = { version = "0.8.0", path = "../esp-alloc", optional = true }
|
||||||
esp-config = { version = "0.5.0", path = "../esp-config" }
|
esp-config = { version = "0.5.0", path = "../esp-config" }
|
||||||
esp-radio-preempt-driver = { version = "0.0.1", path = "../esp-radio-preempt-driver" }
|
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.
|
# Logging interfaces, they are mutually exclusive so they need to be behind separate features.
|
||||||
defmt = { version = "1.0", optional = true }
|
defmt = { version = "1.0", optional = true }
|
||||||
|
@ -153,6 +153,7 @@ impl SchedulerState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(xtensa)]
|
||||||
fn switch_task(&mut self, trap_frame: &mut TrapFrame) {
|
fn switch_task(&mut self, trap_frame: &mut TrapFrame) {
|
||||||
task::save_task_context(unsafe { &mut *self.current_task }, trap_frame);
|
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);
|
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 {
|
fn schedule_task_deletion(&mut self, task: *mut Context) -> bool {
|
||||||
if task.is_null() {
|
if task.is_null() {
|
||||||
self.to_delete = self.current_task;
|
self.to_delete = self.current_task;
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")]
|
#[cfg_attr(riscv, path = "riscv.rs")]
|
||||||
#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
|
#[cfg_attr(xtensa, path = "xtensa.rs")]
|
||||||
mod arch_specific;
|
pub(crate) mod arch_specific;
|
||||||
|
|
||||||
use core::{ffi::c_void, mem::MaybeUninit};
|
use core::{ffi::c_void, mem::MaybeUninit};
|
||||||
|
|
||||||
use allocator_api2::boxed::Box;
|
use allocator_api2::boxed::Box;
|
||||||
|
#[cfg(riscv)]
|
||||||
|
use arch_specific::Registers;
|
||||||
pub(crate) use arch_specific::*;
|
pub(crate) use arch_specific::*;
|
||||||
use esp_hal::trapframe::TrapFrame;
|
use esp_hal::trapframe::TrapFrame;
|
||||||
|
|
||||||
use crate::{InternalMemory, SCHEDULER_STATE, task, timer};
|
use crate::{InternalMemory, SCHEDULER_STATE, task, timer};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
pub(crate) struct Context {
|
pub(crate) struct Context {
|
||||||
trap_frame: TrapFrame,
|
#[cfg(riscv)]
|
||||||
|
pub trap_frame: Registers,
|
||||||
|
#[cfg(xtensa)]
|
||||||
|
pub trap_frame: TrapFrame,
|
||||||
pub thread_semaphore: u32,
|
pub thread_semaphore: u32,
|
||||||
pub next: *mut Context,
|
pub next: *mut Context,
|
||||||
pub _allocated_stack: Box<[MaybeUninit<u8>], InternalMemory>,
|
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.
|
// This context will be filled out by the first context switch.
|
||||||
let context = Box::new_in(
|
let context = Box::new_in(
|
||||||
Context {
|
Context {
|
||||||
|
#[cfg(riscv)]
|
||||||
|
trap_frame: Registers::default(),
|
||||||
|
#[cfg(xtensa)]
|
||||||
trap_frame: TrapFrame::default(),
|
trap_frame: TrapFrame::default(),
|
||||||
thread_semaphore: 0,
|
thread_semaphore: 0,
|
||||||
next: core::ptr::null_mut(),
|
next: core::ptr::null_mut(),
|
||||||
|
@ -1,18 +1,108 @@
|
|||||||
use core::ffi::c_void;
|
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(
|
pub(crate) fn new_task_context(
|
||||||
task: extern "C" fn(*mut c_void),
|
task: extern "C" fn(*mut c_void),
|
||||||
param: *mut c_void,
|
param: *mut c_void,
|
||||||
stack_top: *mut (),
|
stack_top: *mut (),
|
||||||
) -> TrapFrame {
|
) -> Registers {
|
||||||
let stack_top = stack_top as usize;
|
let stack_top = stack_top as usize;
|
||||||
let stack_top = stack_top - (stack_top % 16);
|
let stack_top = stack_top - (stack_top % 16);
|
||||||
|
|
||||||
TrapFrame {
|
Registers {
|
||||||
pc: task as usize,
|
pc: task as usize,
|
||||||
a0: param as usize,
|
a0: param as usize,
|
||||||
sp: stack_top,
|
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) {
|
/// Switch to next task
|
||||||
trap_frame.ra = ctx.trap_frame.ra;
|
///
|
||||||
trap_frame.sp = ctx.trap_frame.sp;
|
/// *MUST* be called from inside an ISR without interrupt nesting.
|
||||||
trap_frame.a0 = ctx.trap_frame.a0;
|
///
|
||||||
trap_frame.a1 = ctx.trap_frame.a1;
|
/// The task switching relies on MEPC and MSTATUS to not get clobbered.
|
||||||
trap_frame.a2 = ctx.trap_frame.a2;
|
/// We save MEPC as the current task's PC and change MEPC to an assembly function
|
||||||
trap_frame.a3 = ctx.trap_frame.a3;
|
/// which will save the current CPU state for the current task (excluding PC) and
|
||||||
trap_frame.a4 = ctx.trap_frame.a4;
|
/// restoring the CPU state from the next task.
|
||||||
trap_frame.a5 = ctx.trap_frame.a5;
|
pub fn task_switch(old_ctx: *mut Registers, new_ctx: *mut Registers) -> bool {
|
||||||
trap_frame.a6 = ctx.trap_frame.a6;
|
// Check that there isn't a switch "in-progress"
|
||||||
trap_frame.a7 = ctx.trap_frame.a7;
|
//
|
||||||
trap_frame.t0 = ctx.trap_frame.t0;
|
// While this shouldn't happen: from observation it does!
|
||||||
trap_frame.t1 = ctx.trap_frame.t1;
|
//
|
||||||
trap_frame.t2 = ctx.trap_frame.t2;
|
// This happens if the timer tick interrupt gets asserted while the task switch is in
|
||||||
trap_frame.t3 = ctx.trap_frame.t3;
|
// progress (i.e. the sw-int is served).
|
||||||
trap_frame.t4 = ctx.trap_frame.t4;
|
//
|
||||||
trap_frame.t5 = ctx.trap_frame.t5;
|
// In that case returning via `mret` will first service the pending interrupt before actually
|
||||||
trap_frame.t6 = ctx.trap_frame.t6;
|
// ending up in `sys_switch`.
|
||||||
trap_frame.s0 = ctx.trap_frame.s0;
|
//
|
||||||
trap_frame.s1 = ctx.trap_frame.s1;
|
// Setting MPIE to 0 _should_ prevent that from happening.
|
||||||
trap_frame.s2 = ctx.trap_frame.s2;
|
if !_NEXT_CTX_PTR
|
||||||
trap_frame.s3 = ctx.trap_frame.s3;
|
.load(portable_atomic::Ordering::SeqCst)
|
||||||
trap_frame.s4 = ctx.trap_frame.s4;
|
.is_null()
|
||||||
trap_frame.s5 = ctx.trap_frame.s5;
|
{
|
||||||
trap_frame.s6 = ctx.trap_frame.s6;
|
return false;
|
||||||
trap_frame.s7 = ctx.trap_frame.s7;
|
}
|
||||||
trap_frame.s8 = ctx.trap_frame.s8;
|
|
||||||
trap_frame.s9 = ctx.trap_frame.s9;
|
_CURRENT_CTX_PTR.store(old_ctx, portable_atomic::Ordering::SeqCst);
|
||||||
trap_frame.s10 = ctx.trap_frame.s10;
|
_NEXT_CTX_PTR.store(new_ctx, portable_atomic::Ordering::SeqCst);
|
||||||
trap_frame.s11 = ctx.trap_frame.s11;
|
|
||||||
trap_frame.gp = ctx.trap_frame.gp;
|
let old = esp_hal::riscv::register::mepc::read();
|
||||||
trap_frame.tp = ctx.trap_frame.tp;
|
unsafe {
|
||||||
trap_frame.pc = ctx.trap_frame.pc;
|
(*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) {
|
core::arch::global_asm!(
|
||||||
ctx.trap_frame.ra = trap_frame.ra;
|
r#"
|
||||||
ctx.trap_frame.sp = trap_frame.sp;
|
.section .trap, "ax"
|
||||||
ctx.trap_frame.a0 = trap_frame.a0;
|
|
||||||
ctx.trap_frame.a1 = trap_frame.a1;
|
.globl sys_switch
|
||||||
ctx.trap_frame.a2 = trap_frame.a2;
|
.align 4
|
||||||
ctx.trap_frame.a3 = trap_frame.a3;
|
sys_switch:
|
||||||
ctx.trap_frame.a4 = trap_frame.a4;
|
# put some regs on the stack since we will need those regs
|
||||||
ctx.trap_frame.a5 = trap_frame.a5;
|
addi sp, sp, -16
|
||||||
ctx.trap_frame.a6 = trap_frame.a6;
|
sw t0, 0(sp)
|
||||||
ctx.trap_frame.a7 = trap_frame.a7;
|
sw t1, 4(sp)
|
||||||
ctx.trap_frame.t0 = trap_frame.t0;
|
|
||||||
ctx.trap_frame.t1 = trap_frame.t1;
|
# t0 => current context
|
||||||
ctx.trap_frame.t2 = trap_frame.t2;
|
la t0, {_CURRENT_CTX_PTR}
|
||||||
ctx.trap_frame.t3 = trap_frame.t3;
|
lw t0, 0(t0)
|
||||||
ctx.trap_frame.t4 = trap_frame.t4;
|
|
||||||
ctx.trap_frame.t5 = trap_frame.t5;
|
# store registers to old context - PC needs to be set by the "caller"
|
||||||
ctx.trap_frame.t6 = trap_frame.t6;
|
sw ra, 0*4(t0)
|
||||||
ctx.trap_frame.s0 = trap_frame.s0;
|
|
||||||
ctx.trap_frame.s1 = trap_frame.s1;
|
lw t1, 0(sp)
|
||||||
ctx.trap_frame.s2 = trap_frame.s2;
|
sw t1, 1*4(t0)
|
||||||
ctx.trap_frame.s3 = trap_frame.s3;
|
|
||||||
ctx.trap_frame.s4 = trap_frame.s4;
|
lw t1, 4(sp)
|
||||||
ctx.trap_frame.s5 = trap_frame.s5;
|
sw t1, 2*4(t0)
|
||||||
ctx.trap_frame.s6 = trap_frame.s6;
|
|
||||||
ctx.trap_frame.s7 = trap_frame.s7;
|
sw t2, 3*4(t0)
|
||||||
ctx.trap_frame.s8 = trap_frame.s8;
|
sw t3, 4*4(t0)
|
||||||
ctx.trap_frame.s9 = trap_frame.s9;
|
sw t4, 5*4(t0)
|
||||||
ctx.trap_frame.s10 = trap_frame.s10;
|
sw t5, 6*4(t0)
|
||||||
ctx.trap_frame.s11 = trap_frame.s11;
|
sw t6, 7*4(t0)
|
||||||
ctx.trap_frame.gp = trap_frame.gp;
|
sw a0, 8*4(t0)
|
||||||
ctx.trap_frame.tp = trap_frame.tp;
|
sw a1, 9*4(t0)
|
||||||
ctx.trap_frame.pc = trap_frame.pc;
|
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(riscv, path = "riscv.rs")]
|
||||||
#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
|
#[cfg_attr(xtensa, path = "xtensa.rs")]
|
||||||
mod arch_specific;
|
mod arch_specific;
|
||||||
|
|
||||||
pub(crate) use arch_specific::*;
|
pub(crate) use arch_specific::*;
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
interrupt::{InterruptHandler, Priority},
|
interrupt::{InterruptHandler, Priority},
|
||||||
sync::Locked,
|
sync::Locked,
|
||||||
trapframe::TrapFrame,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::TimeBase;
|
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
|
// The timer needs to tick at Priority 1 to prevent accidentally interrupting
|
||||||
// priority 1 limited locks.
|
// priority 1 limited locks.
|
||||||
let cb: extern "C" fn() = unsafe { core::mem::transmute(timer_tick_handler as *const ()) };
|
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);
|
let handler = InterruptHandler::new(cb, Priority::Priority1);
|
||||||
|
|
||||||
timer.set_interrupt_handler(handler);
|
timer.set_interrupt_handler(handler);
|
||||||
@ -44,18 +50,3 @@ pub(crate) fn disable_timebase() {
|
|||||||
unwrap!(timer.cancel());
|
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::{
|
use esp_hal::{
|
||||||
interrupt::{self, TrapFrame, software::SoftwareInterrupt},
|
interrupt::{self, software::SoftwareInterrupt},
|
||||||
peripherals::Interrupt,
|
peripherals::Interrupt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::task::task_switch;
|
use crate::task::task_switch;
|
||||||
|
|
||||||
pub(crate) fn setup_multitasking() {
|
pub(crate) fn setup_multitasking() {
|
||||||
unwrap!(interrupt::enable(
|
// Register the interrupt handler without nesting to satisfy the requirements of the task
|
||||||
Interrupt::FROM_CPU_INTR2,
|
// switching code
|
||||||
interrupt::Priority::Priority1,
|
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() {
|
pub(crate) fn disable_multitasking() {
|
||||||
interrupt::disable(esp_hal::system::Cpu::ProCpu, Interrupt::FROM_CPU_INTR2);
|
interrupt::disable(esp_hal::system::Cpu::ProCpu, Interrupt::FROM_CPU_INTR2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
#[esp_hal::ram]
|
#[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
|
// clear FROM_CPU_INTR2
|
||||||
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
||||||
swi.reset();
|
swi.reset();
|
||||||
@ -32,3 +35,9 @@ pub(crate) fn yield_task() {
|
|||||||
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
||||||
swi.raise();
|
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;
|
let intr = SW_INTERRUPT;
|
||||||
unsafe { core::arch::asm!("wsr.intset {0}", in(reg) intr, options(nostack)) };
|
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
|
/// Argument register a7, typically used to pass the eighth argument to a
|
||||||
/// function.
|
/// function.
|
||||||
pub a7: usize,
|
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)
|
/// Trap entry point rust (_start_trap_rust)
|
||||||
@ -514,162 +471,162 @@ _start_trap: // Handle exceptions in vectored mode
|
|||||||
csrr t0, mscratch
|
csrr t0, mscratch
|
||||||
// now SP is in RAM - continue
|
// now SP is in RAM - continue
|
||||||
|
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, _start_trap_rust_hal /* Load the HAL trap handler */
|
la ra, _start_trap_rust_hal /* Load the HAL trap handler */
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap1:
|
_start_trap1:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt1
|
la ra, interrupt1
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap2:
|
_start_trap2:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt2
|
la ra, interrupt2
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap3:
|
_start_trap3:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt3
|
la ra, interrupt3
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap4:
|
_start_trap4:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt4
|
la ra, interrupt4
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap5:
|
_start_trap5:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt5
|
la ra, interrupt5
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap6:
|
_start_trap6:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt6
|
la ra, interrupt6
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap7:
|
_start_trap7:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt7
|
la ra, interrupt7
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap8:
|
_start_trap8:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt8
|
la ra, interrupt8
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap9:
|
_start_trap9:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt9
|
la ra, interrupt9
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap10:
|
_start_trap10:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt10
|
la ra, interrupt10
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap11:
|
_start_trap11:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt11
|
la ra, interrupt11
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap12:
|
_start_trap12:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt12
|
la ra, interrupt12
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap13:
|
_start_trap13:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt13
|
la ra, interrupt13
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap14:
|
_start_trap14:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt14
|
la ra, interrupt14
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap15:
|
_start_trap15:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt15
|
la ra, interrupt15
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap16:
|
_start_trap16:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt16
|
la ra, interrupt16
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap17:
|
_start_trap17:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt17
|
la ra, interrupt17
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap18:
|
_start_trap18:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt18
|
la ra, interrupt18
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap19:
|
_start_trap19:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt19
|
la ra, interrupt19
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap20:
|
_start_trap20:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt20
|
la ra, interrupt20
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap21:
|
_start_trap21:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt21
|
la ra, interrupt21
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap22:
|
_start_trap22:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt22
|
la ra, interrupt22
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap23:
|
_start_trap23:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt23
|
la ra, interrupt23
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap24:
|
_start_trap24:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt24
|
la ra, interrupt24
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap25:
|
_start_trap25:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt25
|
la ra, interrupt25
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap26:
|
_start_trap26:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt26
|
la ra, interrupt26
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap27:
|
_start_trap27:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt27
|
la ra, interrupt27
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap28:
|
_start_trap28:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt28
|
la ra, interrupt28
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap29:
|
_start_trap29:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt29
|
la ra, interrupt29
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap30:
|
_start_trap30:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt30
|
la ra, interrupt30
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
_start_trap31:
|
_start_trap31:
|
||||||
addi sp, sp, -40*4
|
addi sp, sp, -16*4
|
||||||
sw ra, 0(sp)
|
sw ra, 0(sp)
|
||||||
la ra, interrupt31
|
la ra, interrupt31
|
||||||
j _start_trap_direct
|
j _start_trap_direct
|
||||||
@ -692,59 +649,10 @@ r#"
|
|||||||
sw a5, 13*4(sp)
|
sw a5, 13*4(sp)
|
||||||
sw a6, 14*4(sp)
|
sw a6, 14*4(sp)
|
||||||
sw a7, 15*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
|
# jump to handler loaded in direct handler
|
||||||
sw s0, 30*4(sp)
|
add a0, sp, zero # load trap-frame address in a0
|
||||||
|
jalr ra, ra # jump to label loaded in _start_trapX
|
||||||
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
|
|
||||||
|
|
||||||
lw ra, 0*4(sp)
|
lw ra, 0*4(sp)
|
||||||
lw t0, 1*4(sp)
|
lw t0, 1*4(sp)
|
||||||
@ -762,21 +670,8 @@ r#"
|
|||||||
lw a5, 13*4(sp)
|
lw a5, 13*4(sp)
|
||||||
lw a6, 14*4(sp)
|
lw a6, 14*4(sp)
|
||||||
lw a7, 15*4(sp)
|
lw a7, 15*4(sp)
|
||||||
lw s0, 16*4(sp)
|
|
||||||
lw s1, 17*4(sp)
|
addi sp, sp, 16*4
|
||||||
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)
|
|
||||||
|
|
||||||
# SP was restored from the original SP
|
# SP was restored from the original SP
|
||||||
mret
|
mret
|
||||||
|
Loading…
x
Reference in New Issue
Block a user