Skip to content

Commit

Permalink
feat(threads): xtensa support
Browse files Browse the repository at this point in the history
  • Loading branch information
elenaf9 committed Sep 6, 2024
1 parent ef9ffe2 commit 02c2b06
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/riot-rs-threads/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ esp-hal = { workspace = true, features = ["esp32c3"] }
[target.'cfg(context = "esp32c6")'.dependencies]
esp-hal = { workspace = true, features = ["esp32c6"] }

[target.'cfg(context = "esp32s3")'.dependencies]
esp-hal = { workspace = true, features = ["esp32s3"] }

[target.'cfg(context = "cortex-m")'.dependencies]
# cortex-m specifics
cortex-m.workspace = true
Expand Down
9 changes: 7 additions & 2 deletions src/riot-rs-threads/src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,21 @@ pub trait Arch {
/// Setup and initiate the first context switch.
fn start_threading();
}
mod xtensa;

cfg_if::cfg_if! {
if #[cfg(context = "cortex-m")] {
if #[cfg(target_arch = "cortex-m")] {
mod cortex_m;
pub use cortex_m::Cpu;
}
else if #[cfg(any(context = "esp32c3", context = "esp32c6"))] {
else if #[cfg(target_arch = "riscv32")] {
mod riscv;
pub use riscv::Cpu;
}
else if #[cfg(target_arch = "xtensa")] {
// mod xtensa;
pub use xtensa::Cpu;
}
else {
pub struct Cpu;
impl Arch for Cpu {
Expand Down
163 changes: 163 additions & 0 deletions src/riot-rs-threads/src/arch/xtensa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use esp_hal::{trapframe::TrapFrame, xtensa_lx, xtensa_lx_rt};

use crate::{cleanup, Arch, THREADS};

const SOFTWARE0_INTR_NR: usize = 7;

pub struct Cpu;

impl Arch for Cpu {
type ThreadData = TrapFrame;
const DEFAULT_THREAD_DATA: Self::ThreadData = default_trap_frame();

fn schedule() {
let intr = 1 << SOFTWARE0_INTR_NR;
unsafe {
core::arch::asm!("wsr.intset {0}", in(reg) intr, options(nostack));
}
}

fn setup_stack(thread: &mut crate::thread::Thread, stack: &mut [u8], func: usize, arg: usize) {
let stack_start = stack.as_ptr() as usize;
let task_stack_ptr = stack_start + stack.len();
// 16 byte alignment.
let stack_pos = task_stack_ptr - (task_stack_ptr % 0x10);

thread.sp = stack_pos;
thread.data.A1 = stack_pos as u32;
thread.data.A6 = arg as u32;
// Usually A0 holds the return address.
// However, xtensa features so-called Windowed registers, which allow
// to shift the used registers when calling procedure.
// The xtensa-lx-rt does this when calling the exception handler using
// call4, which shifts the window by 4.
// See `xtensa_lx_rt::exception::asm::__default_naked_exception`.
// (At least that's what I assume is happening)
thread.data.A4 = cleanup as u32;
thread.data.PC = func as u32;

// Copied from esp-wifi::preempt::preempt_xtensa

// For windowed ABI set WOE and CALLINC (pretend task was 'call4'd).
thread.data.PS = 0x00040000 | (1 & 3) << 16;
}

fn start_threading() {
let enabled = xtensa_lx::interrupt::disable();
unsafe {
xtensa_lx::interrupt::enable_mask(
1 << SOFTWARE0_INTR_NR // Software1
| xtensa_lx_rt::interrupt::CpuInterruptLevel::Level2.mask()
| xtensa_lx_rt::interrupt::CpuInterruptLevel::Level6.mask()
| enabled,
);
}
Self::schedule();
}
}

const fn default_trap_frame() -> TrapFrame {
TrapFrame {
PC: 0,
PS: 0,
A0: 0,
A1: 0,
A2: 0,
A3: 0,
A4: 0,
A5: 0,
A6: 0,
A7: 0,
A8: 0,
A9: 0,
A10: 0,
A11: 0,
A12: 0,
A13: 0,
A14: 0,
A15: 0,
SAR: 0,
EXCCAUSE: 0,
EXCVADDR: 0,
LBEG: 0,
LEND: 0,
LCOUNT: 0,
THREADPTR: 0,
SCOMPARE1: 0,
BR: 0,
ACCLO: 0,
ACCHI: 0,
M0: 0,
M1: 0,
M2: 0,
M3: 0,
F64R_LO: 0,
F64R_HI: 0,
F64S: 0,
FCR: 0,
FSR: 0,
F0: 0,
F1: 0,
F2: 0,
F3: 0,
F4: 0,
F5: 0,
F6: 0,
F7: 0,
F8: 0,
F9: 0,
F10: 0,
F11: 0,
F12: 0,
F13: 0,
F14: 0,
F15: 0,
}
}

#[allow(non_snake_case)]
#[no_mangle]
fn Software0(_level: u32, context: &mut TrapFrame) {
let intr = 1 << SOFTWARE0_INTR_NR;
unsafe {
core::arch::asm!("wsr.intclear {0}", in(reg) intr, options(nostack));
}

unsafe {
sched(context);
}
}

/// Probes the runqueue for the next thread and switches context if needed.
///
/// # Safety
///
/// This method might switch the current register state that is saved in the
/// `trap_frame`.
/// It should only be called from inside the trap handler that is responsible for
/// context switching.
unsafe fn sched(trap_frame: &mut TrapFrame) {
loop {
if THREADS.with_mut(|mut threads| {
let next_pid = match threads.runqueue.get_next() {
Some(pid) => pid,
None => {
unsafe { core::arch::asm!("waiti 0") };
return false;
}
};

if let Some(current_pid) = threads.current_pid() {
if next_pid == current_pid {
return true;
}
threads.threads[usize::from(current_pid)].data = *trap_frame;
}
threads.current_thread = Some(next_pid);
*trap_frame = threads.threads[usize::from(next_pid)].data;
true
}) {
break;
}
}
}
1 change: 1 addition & 0 deletions src/riot-rs-threads/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![feature(naked_functions)]
#![feature(used_with_arg)]
#![feature(lint_reasons)]
#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))]
// Disable indexing lints for now, possible panics are documented or rely on internally-enforced
// invariants
#![allow(clippy::indexing_slicing)]
Expand Down

0 comments on commit 02c2b06

Please sign in to comment.