mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-27 12:20:56 +00:00
esp-radio: remove scheduler lifecycle management, remove idle task (#4093)
* Start scheduler on preempt init, remove deinit * Static-allocate the main task context * taskless idle * Remove the need for an idle task
This commit is contained in:
parent
c1c226a20d
commit
9d596ca752
@ -1,13 +1,13 @@
|
||||
//! This crate allows using esp-radio on top of esp-hal, without any other OS.
|
||||
//!
|
||||
//! This crate needs to be initialized with an esp-hal timer before the scheduler can be started.
|
||||
//! This crate requires an esp-hal timer to operate.
|
||||
//!
|
||||
//! ```rust, no_run
|
||||
#![doc = esp_hal::before_snippet!()]
|
||||
//! use esp_hal::timer::timg::TimerGroup;
|
||||
//!
|
||||
//! let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
//! esp_preempt::init(timg0.timer0);
|
||||
//! esp_preempt::start(timg0.timer0);
|
||||
//!
|
||||
//! // You can now start esp-radio:
|
||||
//! // let esp_radio_controller = esp_radio::init().unwrap();
|
||||
@ -35,7 +35,6 @@ mod wait_queue;
|
||||
pub(crate) use esp_alloc::InternalMemory;
|
||||
use esp_hal::{
|
||||
Blocking,
|
||||
system::Cpu,
|
||||
timer::{AnyTimer, OneShotTimer},
|
||||
};
|
||||
pub(crate) use scheduler::SCHEDULER;
|
||||
@ -102,7 +101,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the scheduler.
|
||||
/// Starts the scheduler.
|
||||
///
|
||||
/// # The `timer` argument
|
||||
///
|
||||
@ -116,12 +115,19 @@ where
|
||||
/// For an example, see the [crate-level documentation][self].
|
||||
#[cfg_attr(
|
||||
multi_core,
|
||||
doc = " \nNote that `esp_radio::init()` must be called on the same core as `esp_preempt::init()`."
|
||||
doc = " \nNote that `esp_radio::init()` must be called on the same core as `esp_preempt::start()`."
|
||||
)]
|
||||
pub fn init(timer: impl TimerSource) {
|
||||
pub fn start(timer: impl TimerSource) {
|
||||
SCHEDULER.with(move |scheduler| {
|
||||
scheduler.time_driver = Some(TimeDriver::new(timer.timer()));
|
||||
scheduler.runs_on = Cpu::current();
|
||||
scheduler.setup(TimeDriver::new(timer.timer()));
|
||||
|
||||
// allocate the default tasks
|
||||
task::allocate_main_task(scheduler);
|
||||
|
||||
task::setup_multitasking();
|
||||
|
||||
unwrap!(scheduler.time_driver.as_mut()).start();
|
||||
task::yield_task();
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ use crate::{
|
||||
TaskPtr,
|
||||
},
|
||||
timer::TimeDriver,
|
||||
timer_queue,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
@ -69,6 +68,7 @@ pub(crate) struct SchedulerState {
|
||||
|
||||
/// Pointer to the current task.
|
||||
pub(crate) current_task: Option<TaskPtr>,
|
||||
idle_context: CpuContext,
|
||||
|
||||
/// A list of all allocated tasks
|
||||
pub(crate) all_tasks: TaskList<TaskAllocListElement>,
|
||||
@ -91,6 +91,7 @@ impl SchedulerState {
|
||||
Self {
|
||||
runs_on: Cpu::ProCpu,
|
||||
current_task: None,
|
||||
idle_context: CpuContext::new(),
|
||||
all_tasks: TaskList::new(),
|
||||
run_queue: RunQueue::new(),
|
||||
to_delete: TaskList::new(),
|
||||
@ -100,6 +101,36 @@ impl SchedulerState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn setup(&mut self, time_driver: TimeDriver) {
|
||||
self.time_driver = Some(time_driver);
|
||||
self.runs_on = Cpu::current();
|
||||
task::set_idle_hook_entry(&mut self.idle_context);
|
||||
}
|
||||
|
||||
pub(crate) fn create_task(
|
||||
&mut self,
|
||||
task: extern "C" fn(*mut c_void),
|
||||
param: *mut c_void,
|
||||
task_stack_size: usize,
|
||||
priority: usize,
|
||||
) -> TaskPtr {
|
||||
let mut task = Box::new_in(
|
||||
Task::new(task, param, task_stack_size, priority),
|
||||
InternalMemory,
|
||||
);
|
||||
task.heap_allocated = true;
|
||||
let task_ptr = NonNull::from(Box::leak(task));
|
||||
|
||||
self.all_tasks.push(task_ptr);
|
||||
if self.run_queue.mark_task_ready(task_ptr) {
|
||||
task::yield_task();
|
||||
}
|
||||
|
||||
debug!("Task created: {:?}", task_ptr);
|
||||
|
||||
task_ptr
|
||||
}
|
||||
|
||||
fn delete_marked_tasks(&mut self) {
|
||||
while let Some(to_delete) = self.to_delete.pop() {
|
||||
trace!("delete_marked_tasks {:?}", to_delete);
|
||||
@ -112,10 +143,12 @@ impl SchedulerState {
|
||||
}
|
||||
|
||||
fn run_scheduler(&mut self, task_switch: impl FnOnce(*mut CpuContext, *mut CpuContext)) {
|
||||
let mut priority = {
|
||||
let current_task = unsafe { unwrap!(self.current_task).as_ref() };
|
||||
let mut priority = if let Some(current_task) = self.current_task {
|
||||
let current_task = unsafe { current_task.as_ref() };
|
||||
current_task.ensure_no_stack_overflow();
|
||||
current_task.priority
|
||||
Some(current_task.priority)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.delete_marked_tasks();
|
||||
@ -146,20 +179,8 @@ impl SchedulerState {
|
||||
self.run_queue.mark_task_ready(current_task);
|
||||
}
|
||||
|
||||
// At least one task must be ready to run. If there are none, we can't do anything - we
|
||||
// can't just WFI from an interrupt handler. We should create an idle task that WFIs for us,
|
||||
// and can be replaced with auto light sleep.
|
||||
let mut next_task = unwrap!(self.run_queue.pop(), "There are no tasks ready to run.");
|
||||
|
||||
// Were we able to select a new task?
|
||||
if Some(next_task) != self.current_task {
|
||||
debug_assert_eq!(
|
||||
next_task.state(),
|
||||
task::TaskState::Ready,
|
||||
"task: {:?}",
|
||||
next_task
|
||||
);
|
||||
|
||||
let next_task = self.run_queue.pop();
|
||||
if next_task != self.current_task {
|
||||
trace!("Switching task {:?} -> {:?}", self.current_task, next_task);
|
||||
|
||||
// If the current task is deleted, we can skip saving its context. We signal this by
|
||||
@ -169,19 +190,48 @@ impl SchedulerState {
|
||||
} else {
|
||||
core::ptr::null_mut()
|
||||
};
|
||||
let next_context = unsafe { &raw mut next_task.as_mut().cpu_context };
|
||||
|
||||
let next_context = if let Some(mut next) = next_task {
|
||||
priority = Some(next.priority(&mut self.run_queue));
|
||||
unsafe { &raw mut next.as_mut().cpu_context }
|
||||
} else if current_context.is_null() {
|
||||
// The current task has been deleted, we can't use its stack. Let's assume the main
|
||||
// task's context has a valid stack pointer for us.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(xtensa)] {
|
||||
self.idle_context.A1 = unsafe { task::MAIN_TASK.cpu_context.A1 };
|
||||
} else {
|
||||
self.idle_context.sp = unsafe { task::MAIN_TASK.cpu_context.sp };
|
||||
}
|
||||
}
|
||||
&raw mut self.idle_context
|
||||
} else {
|
||||
// As the stack pointer, let's just use the current value.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(xtensa)] {
|
||||
self.idle_context.A1 = esp_hal::xtensa_lx::get_stack_pointer() as u32;
|
||||
} else {
|
||||
let current_sp;
|
||||
unsafe { core::arch::asm!("mv {0}, sp", out(reg) current_sp); }
|
||||
self.idle_context.sp = current_sp;
|
||||
}
|
||||
}
|
||||
&raw mut self.idle_context
|
||||
};
|
||||
|
||||
task_switch(current_context, next_context);
|
||||
|
||||
self.current_task = Some(next_task);
|
||||
priority = next_task.priority(&mut self.run_queue);
|
||||
// If we went to idle, this will be None and we won't mess up the main task's stack.
|
||||
self.current_task = next_task;
|
||||
}
|
||||
|
||||
// TODO maybe we don't need to do this every time?
|
||||
// The current task is not in the run queue. If the run queue on the current priority level
|
||||
// is empty, the current task is the only one running at its priority level. In this
|
||||
// case, we don't need time slicing.
|
||||
let arm_next_timeslice_tick = !self.run_queue.is_level_empty(priority);
|
||||
let arm_next_timeslice_tick = priority
|
||||
.map(|p| !self.run_queue.is_level_empty(p))
|
||||
.unwrap_or(false);
|
||||
time_driver.arm_next_wakeup(arm_next_timeslice_tick);
|
||||
}
|
||||
|
||||
@ -229,12 +279,16 @@ impl SchedulerState {
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_task(&mut self, to_delete: TaskPtr) {
|
||||
fn delete_task(&mut self, mut to_delete: TaskPtr) {
|
||||
self.remove_from_all_queues(to_delete);
|
||||
|
||||
unsafe {
|
||||
let task = Box::from_raw_in(to_delete.as_ptr(), InternalMemory);
|
||||
core::mem::drop(task);
|
||||
if to_delete.as_ref().heap_allocated {
|
||||
let task = Box::from_raw_in(to_delete.as_ptr(), InternalMemory);
|
||||
core::mem::drop(task);
|
||||
} else {
|
||||
to_delete.as_mut().thread_semaphore = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,26 +326,11 @@ impl Scheduler {
|
||||
task_stack_size: usize,
|
||||
priority: u32,
|
||||
) -> TaskPtr {
|
||||
let task = Box::new_in(
|
||||
Task::new(task, param, task_stack_size, priority as usize),
|
||||
InternalMemory,
|
||||
);
|
||||
let task_ptr = NonNull::from(Box::leak(task));
|
||||
|
||||
SCHEDULER.with(|state| {
|
||||
state.all_tasks.push(task_ptr);
|
||||
if state.run_queue.mark_task_ready(task_ptr) {
|
||||
task::yield_task();
|
||||
}
|
||||
});
|
||||
|
||||
debug!("Task created: {:?}", task_ptr);
|
||||
|
||||
task_ptr
|
||||
self.with(|state| state.create_task(task, param, task_stack_size, priority as usize))
|
||||
}
|
||||
|
||||
pub(crate) fn sleep_until(&self, wake_at: Instant) -> bool {
|
||||
SCHEDULER.with(|scheduler| scheduler.sleep_until(wake_at))
|
||||
self.with(|scheduler| scheduler.sleep_until(wake_at))
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,28 +359,6 @@ impl esp_radio_preempt_driver::Scheduler for Scheduler {
|
||||
})
|
||||
}
|
||||
|
||||
fn enable(&self) {
|
||||
// allocate the default tasks
|
||||
task::allocate_main_task();
|
||||
task::spawn_idle_task();
|
||||
|
||||
task::setup_multitasking();
|
||||
|
||||
self.with(|scheduler| unwrap!(scheduler.time_driver.as_mut()).start());
|
||||
task::yield_task();
|
||||
}
|
||||
|
||||
fn disable(&self) {
|
||||
self.with(|scheduler| unwrap!(scheduler.time_driver.as_mut()).stop());
|
||||
task::disable_multitasking();
|
||||
|
||||
// Note that deleting tasks leaks resources, because we don't know how to free memory
|
||||
// allocated by the deleted tasks.
|
||||
task::delete_all_tasks();
|
||||
|
||||
timer_queue::reset();
|
||||
}
|
||||
|
||||
fn yield_task(&self) {
|
||||
task::yield_task();
|
||||
}
|
||||
|
@ -8,7 +8,13 @@ use allocator_api2::boxed::Box;
|
||||
pub(crate) use arch_specific::*;
|
||||
use esp_radio_preempt_driver::semaphore::{SemaphoreHandle, SemaphorePtr};
|
||||
|
||||
use crate::{InternalMemory, SCHEDULER, run_queue::RunQueue, wait_queue::WaitQueue};
|
||||
use crate::{
|
||||
InternalMemory,
|
||||
SCHEDULER,
|
||||
run_queue::RunQueue,
|
||||
scheduler::SchedulerState,
|
||||
wait_queue::WaitQueue,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
@ -195,7 +201,7 @@ pub(crate) struct Task {
|
||||
pub cpu_context: CpuContext,
|
||||
pub thread_semaphore: Option<SemaphorePtr>,
|
||||
pub state: TaskState,
|
||||
pub _allocated_stack: Box<[MaybeUninit<u32>], InternalMemory>,
|
||||
pub _allocated_stack: *mut [MaybeUninit<u32>],
|
||||
pub priority: usize,
|
||||
|
||||
pub wakeup_at: u64,
|
||||
@ -215,6 +221,9 @@ pub(crate) struct Task {
|
||||
|
||||
/// The list of tasks scheduled for deletion
|
||||
pub delete_list_item: TaskListItem,
|
||||
|
||||
/// Whether the task was allocated on the heap.
|
||||
pub(crate) heap_allocated: bool,
|
||||
}
|
||||
|
||||
const STACK_CANARY: u32 = 0xDEEDBAAD;
|
||||
@ -233,11 +242,11 @@ impl Task {
|
||||
|
||||
let task_stack_size_words = task_stack_size / 4;
|
||||
let mut stack = Box::<[u32], _>::new_uninit_slice_in(task_stack_size_words, InternalMemory);
|
||||
|
||||
let stack_top = unsafe { stack.as_mut_ptr().add(task_stack_size_words).cast() };
|
||||
|
||||
stack[0] = MaybeUninit::new(STACK_CANARY);
|
||||
|
||||
let stack = Box::leak(stack) as *mut [MaybeUninit<u32>];
|
||||
let stack_top = unsafe { stack.cast::<u32>().add(task_stack_size_words).cast() };
|
||||
|
||||
Task {
|
||||
cpu_context: new_task_context(task_fn, param, stack_top),
|
||||
thread_semaphore: None,
|
||||
@ -252,16 +261,21 @@ impl Task {
|
||||
ready_queue_item: TaskListItem::None,
|
||||
timer_queue_item: TaskListItem::None,
|
||||
delete_list_item: TaskListItem::None,
|
||||
|
||||
heap_allocated: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_no_stack_overflow(&self) {
|
||||
// TODO: fix this for main task
|
||||
if self._allocated_stack.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
unsafe { self._allocated_stack[0].assume_init() },
|
||||
// This cast is safe to do from MaybeUninit<u32> because this is the word we've written
|
||||
// during initialization.
|
||||
unsafe { self._allocated_stack.cast::<u32>().read() },
|
||||
STACK_CANARY,
|
||||
"Stack overflow detected in {:?}",
|
||||
self as *const Task
|
||||
@ -279,82 +293,48 @@ impl Drop for Task {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn allocate_main_task() {
|
||||
// This context will be filled out by the first context switch.
|
||||
let task = Box::new_in(
|
||||
Task {
|
||||
cpu_context: CpuContext::default(),
|
||||
thread_semaphore: None,
|
||||
state: TaskState::Ready,
|
||||
_allocated_stack: Box::<[u32], _>::new_uninit_slice_in(0, InternalMemory),
|
||||
current_queue: None,
|
||||
priority: 1,
|
||||
// This context will be filled out by the first context switch.
|
||||
// We allocate the main task statically, because there is always a main task. If deleted, we simply
|
||||
// don't deallocate this.
|
||||
pub(crate) static mut MAIN_TASK: Task = Task {
|
||||
cpu_context: CpuContext::new(),
|
||||
thread_semaphore: None,
|
||||
state: TaskState::Ready,
|
||||
_allocated_stack: core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0),
|
||||
current_queue: None,
|
||||
priority: 0,
|
||||
|
||||
wakeup_at: 0,
|
||||
wakeup_at: 0,
|
||||
|
||||
alloc_list_item: TaskListItem::None,
|
||||
ready_queue_item: TaskListItem::None,
|
||||
timer_queue_item: TaskListItem::None,
|
||||
delete_list_item: TaskListItem::None,
|
||||
},
|
||||
InternalMemory,
|
||||
);
|
||||
let main_task_ptr = NonNull::from(Box::leak(task));
|
||||
alloc_list_item: TaskListItem::None,
|
||||
ready_queue_item: TaskListItem::None,
|
||||
timer_queue_item: TaskListItem::None,
|
||||
delete_list_item: TaskListItem::None,
|
||||
|
||||
heap_allocated: false,
|
||||
};
|
||||
|
||||
pub(super) fn allocate_main_task(scheduler: &mut SchedulerState) {
|
||||
let mut main_task_ptr = unwrap!(NonNull::new(&raw mut MAIN_TASK));
|
||||
debug!("Main task created: {:?}", main_task_ptr);
|
||||
|
||||
SCHEDULER.with(|state| {
|
||||
debug_assert!(
|
||||
state.current_task.is_none(),
|
||||
"Tried to allocate main task multiple times"
|
||||
);
|
||||
unsafe {
|
||||
let main_task = main_task_ptr.as_mut();
|
||||
|
||||
// The main task is already running, no need to add it to the ready queue.
|
||||
state.all_tasks.push(main_task_ptr);
|
||||
state.current_task = Some(main_task_ptr);
|
||||
state.run_queue.mark_task_ready(main_task_ptr);
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn spawn_idle_task() {
|
||||
let ptr = SCHEDULER.create_task(idle_task, core::ptr::null_mut(), 4096, 0);
|
||||
debug!("Idle task created: {:?}", ptr);
|
||||
}
|
||||
|
||||
pub(crate) extern "C" fn idle_task(_: *mut c_void) {
|
||||
loop {
|
||||
// TODO: make this configurable.
|
||||
#[cfg(xtensa)]
|
||||
unsafe {
|
||||
core::arch::asm!("waiti 0");
|
||||
}
|
||||
#[cfg(riscv)]
|
||||
unsafe {
|
||||
core::arch::asm!("wfi");
|
||||
}
|
||||
// Reset main task properties. The rest should be cleared when the task is deleted.
|
||||
main_task.priority = 0;
|
||||
main_task.state = TaskState::Ready;
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn delete_all_tasks() {
|
||||
trace!("delete_all_tasks");
|
||||
let mut all_tasks = SCHEDULER.with(|state| {
|
||||
// Since we delete all tasks, we walk through the allocation list - we just need to clear
|
||||
// the lists.
|
||||
state.to_delete = TaskList::new();
|
||||
state.run_queue = RunQueue::new();
|
||||
debug_assert!(
|
||||
scheduler.current_task.is_none(),
|
||||
"Tried to allocate main task multiple times"
|
||||
);
|
||||
|
||||
// Clear the current task.
|
||||
state.current_task = None;
|
||||
|
||||
// Take the allocation list
|
||||
core::mem::take(&mut state.all_tasks)
|
||||
});
|
||||
|
||||
while let Some(task) = all_tasks.pop() {
|
||||
unsafe {
|
||||
let task = Box::from_raw_in(task.as_ptr(), InternalMemory);
|
||||
core::mem::drop(task);
|
||||
}
|
||||
}
|
||||
// The main task is already running, no need to add it to the ready queue.
|
||||
scheduler.all_tasks.push(main_task_ptr);
|
||||
scheduler.current_task = Some(main_task_ptr);
|
||||
scheduler.run_queue.mark_task_ready(main_task_ptr);
|
||||
}
|
||||
|
||||
pub(super) fn with_current_task<R>(mut cb: impl FnMut(&mut Task) -> R) -> R {
|
||||
|
@ -1,10 +1,6 @@
|
||||
use core::ffi::c_void;
|
||||
|
||||
use esp_hal::{
|
||||
interrupt::{self, software::SoftwareInterrupt},
|
||||
peripherals::Interrupt,
|
||||
riscv::register,
|
||||
};
|
||||
use esp_hal::{interrupt::software::SoftwareInterrupt, riscv::register};
|
||||
|
||||
use crate::SCHEDULER;
|
||||
|
||||
@ -102,6 +98,25 @@ pub struct CpuContext {
|
||||
pub mstatus: usize,
|
||||
}
|
||||
|
||||
impl CpuContext {
|
||||
/// Creates a new, zeroed out context.
|
||||
pub const fn new() -> Self {
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn idle_hook() -> ! {
|
||||
loop {
|
||||
unsafe { core::arch::asm!("wfi") };
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_idle_hook_entry(idle_context: &mut CpuContext) {
|
||||
// Point idle context PC at the assembly that calls the idle hook. We need a new stack
|
||||
// frame for the idle task on the main stack.
|
||||
idle_context.pc = idle_hook as usize;
|
||||
}
|
||||
|
||||
pub(crate) fn new_task_context(
|
||||
task: extern "C" fn(*mut c_void),
|
||||
param: *mut c_void,
|
||||
@ -286,10 +301,6 @@ pub(crate) fn setup_multitasking() {
|
||||
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);
|
||||
}
|
||||
|
||||
#[esp_hal::ram]
|
||||
extern "C" fn swint2_handler() {
|
||||
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
||||
|
@ -5,6 +5,27 @@ use esp_hal::{xtensa_lx, xtensa_lx_rt};
|
||||
|
||||
use crate::SCHEDULER;
|
||||
|
||||
extern "C" fn idle_hook() -> ! {
|
||||
loop {
|
||||
unsafe { core::arch::asm!("waiti 0") };
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn idle_entry() -> ! {
|
||||
core::arch::naked_asm!("call4 {idle_hook}", idle_hook = sym idle_hook);
|
||||
}
|
||||
|
||||
pub(crate) fn set_idle_hook_entry(idle_context: &mut CpuContext) {
|
||||
// Point idle context PC at the assembly that calls the idle hook. We need a new stack
|
||||
// frame for the idle task on the main stack.
|
||||
idle_context.PC = idle_entry as usize as u32;
|
||||
// Set a valid processor status value
|
||||
let current_ps;
|
||||
unsafe { core::arch::asm!("rsr.ps {0}", out(reg) current_ps, options(nostack)) };
|
||||
idle_context.PS = current_ps;
|
||||
}
|
||||
|
||||
pub(crate) fn new_task_context(
|
||||
task_fn: extern "C" fn(*mut c_void),
|
||||
param: *mut c_void,
|
||||
@ -61,10 +82,6 @@ pub(crate) fn setup_multitasking() {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn disable_multitasking() {
|
||||
xtensa_lx::interrupt::disable_mask(SW_INTERRUPT);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[esp_hal::ram]
|
||||
#[cfg_attr(not(esp32), unsafe(export_name = "Software0"))]
|
||||
|
@ -86,11 +86,6 @@ impl TimeDriver {
|
||||
self.timer.listen();
|
||||
}
|
||||
|
||||
pub(crate) fn stop(&mut self) {
|
||||
self.timer.unlisten();
|
||||
self.timer.stop();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_alarm(&mut self, mut on_task_ready: impl FnMut(TaskPtr)) {
|
||||
let mut timer_queue = core::mem::take(&mut self.timer_queue);
|
||||
|
||||
|
@ -323,12 +323,6 @@ impl TimerImplementation for Timer {
|
||||
|
||||
register_timer_implementation!(Timer);
|
||||
|
||||
pub(crate) fn reset() {
|
||||
TIMER_QUEUE.inner.with(|q| {
|
||||
*q = TimerQueueInner::new();
|
||||
});
|
||||
}
|
||||
|
||||
/// Entry point for the timer task responsible for handling scheduled timer
|
||||
/// events.
|
||||
///
|
||||
|
@ -34,8 +34,6 @@ use crate::semaphore::SemaphorePtr;
|
||||
|
||||
unsafe extern "Rust" {
|
||||
fn esp_preempt_initialized() -> bool;
|
||||
fn esp_preempt_enable();
|
||||
fn esp_preempt_disable();
|
||||
fn esp_preempt_yield_task();
|
||||
fn esp_preempt_yield_task_from_isr();
|
||||
fn esp_preempt_current_task() -> *mut c_void;
|
||||
@ -68,18 +66,6 @@ macro_rules! scheduler_impl {
|
||||
<$t as $crate::Scheduler>::initialized(&$name)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[inline]
|
||||
fn esp_preempt_enable() {
|
||||
<$t as $crate::Scheduler>::enable(&$name)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[inline]
|
||||
fn esp_preempt_disable() {
|
||||
<$t as $crate::Scheduler>::disable(&$name)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[inline]
|
||||
fn esp_preempt_yield_task() {
|
||||
@ -166,14 +152,6 @@ macro_rules! scheduler_impl {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
///
|
||||
/// fn enable(&self) {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
///
|
||||
/// fn disable(&self) {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
///
|
||||
/// fn yield_task(&self) {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
@ -225,12 +203,6 @@ pub trait Scheduler: Send + Sync + 'static {
|
||||
/// up.
|
||||
fn initialized(&self) -> bool;
|
||||
|
||||
/// This function is called by `esp-radio` to start the task scheduler.
|
||||
fn enable(&self);
|
||||
|
||||
/// This function is called by `esp-radio` to stop the task scheduler.
|
||||
fn disable(&self);
|
||||
|
||||
/// This function is called by `esp_radio` to yield control to another task.
|
||||
fn yield_task(&self);
|
||||
|
||||
@ -287,18 +259,6 @@ pub fn initialized() -> bool {
|
||||
unsafe { esp_preempt_initialized() }
|
||||
}
|
||||
|
||||
/// Starts the task scheduler.
|
||||
#[inline]
|
||||
pub fn enable() {
|
||||
unsafe { esp_preempt_enable() }
|
||||
}
|
||||
|
||||
/// Stops the task scheduler.
|
||||
#[inline]
|
||||
pub fn disable() {
|
||||
unsafe { esp_preempt_disable() }
|
||||
}
|
||||
|
||||
/// Yields control to another task.
|
||||
#[inline]
|
||||
pub fn yield_task() {
|
||||
|
@ -11,7 +11,7 @@ Furthermore, `esp_wifi::init` no longer requires `RNG` or a timer.
|
||||
|
||||
```diff
|
||||
-let esp_wifi_ctrl = esp_wifi::init(timg0.timer0, Rng::new()).unwrap();
|
||||
+esp_preempt::init(timg0.timer0);
|
||||
+esp_preempt::start(timg0.timer0);
|
||||
+let esp_wifi_ctrl = esp_radio::init().unwrap();
|
||||
```
|
||||
|
||||
|
@ -229,9 +229,6 @@ impl Drop for Controller<'_> {
|
||||
|
||||
shutdown_radio_isr();
|
||||
|
||||
// This shuts down the task switcher and timer tick interrupt.
|
||||
preempt::disable();
|
||||
|
||||
#[cfg(esp32)]
|
||||
// Allow using `ADC2` again
|
||||
release_adc2(unsafe { esp_hal::Internal::conjure() });
|
||||
@ -271,7 +268,7 @@ impl Drop for Controller<'_> {
|
||||
/// use esp_hal::timer::timg::TimerGroup;
|
||||
///
|
||||
/// let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
/// esp_preempt::init(timg0.timer0);
|
||||
/// esp_preempt::start(timg0.timer0);
|
||||
///
|
||||
/// // You can now start esp-radio:
|
||||
/// let esp_radio_controller = esp_radio::init().unwrap();
|
||||
@ -303,9 +300,6 @@ pub fn init<'d>() -> Result<Controller<'d>, InitializationError> {
|
||||
|
||||
setup_radio_isr();
|
||||
|
||||
// This initializes the task switcher
|
||||
preempt::enable();
|
||||
|
||||
wifi_set_log_verbose();
|
||||
init_radio_clocks();
|
||||
|
||||
|
@ -22,7 +22,7 @@ async fn main(_s: Spawner) {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
|
||||
let radio = RADIO.init(esp_radio::init().unwrap());
|
||||
|
@ -26,7 +26,7 @@ async fn main(_s: Spawner) {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
|
||||
let radio = RADIO.init(esp_radio::init().unwrap());
|
||||
|
@ -40,7 +40,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
||||
|
||||
|
@ -41,7 +41,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
||||
|
||||
|
@ -27,7 +27,7 @@ fn main() -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -38,7 +38,7 @@ fn main() -> ! {
|
||||
let delay = Delay::new();
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -43,7 +43,7 @@ fn main() -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
// Set event handlers for wifi before init to avoid missing any.
|
||||
let mut connections = 0u32;
|
||||
|
@ -47,7 +47,7 @@ fn main() -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -69,7 +69,7 @@ fn main() -> ! {
|
||||
}
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -44,7 +44,7 @@ fn main() -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -58,7 +58,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
||||
|
||||
|
@ -73,7 +73,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
||||
|
||||
|
@ -49,7 +49,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
||||
|
||||
|
@ -33,7 +33,7 @@ fn main() -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -81,7 +81,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
||||
|
||||
|
@ -41,7 +41,7 @@ fn main() -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -26,7 +26,7 @@ fn _esp_radio_can_be_reinited() {
|
||||
let p = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
{
|
||||
let _init = esp_radio::init().unwrap();
|
||||
@ -58,7 +58,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_controller_comms(peripherals: Peripherals) {
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
let init = esp_radio::init().unwrap();
|
||||
|
||||
let mut connector = BleConnector::new(&init, peripherals.BT);
|
||||
@ -108,7 +108,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dropping_controller_during_reset(peripherals: Peripherals) {
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
let init = esp_radio::init().unwrap();
|
||||
|
||||
let mut connector = BleConnector::new(&init, peripherals.BT);
|
||||
|
@ -94,7 +94,7 @@ mod tests {
|
||||
#[test]
|
||||
fn fpu_stays_enabled_with_wifi(peripherals: Peripherals) {
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
let _init = esp_radio::init().unwrap();
|
||||
|
||||
let mut sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||
@ -138,7 +138,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
let _init = esp_radio::init().unwrap();
|
||||
|
||||
let mut sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
use esp_hal::riscv::interrupt::free as interrupt_free;
|
||||
@ -16,6 +17,7 @@ use esp_hal::{
|
||||
clock::CpuClock,
|
||||
interrupt::{Priority, software::SoftwareInterruptControl},
|
||||
peripherals::{Peripherals, TIMG0},
|
||||
time::{Duration, Instant},
|
||||
timer::timg::TimerGroup,
|
||||
};
|
||||
use esp_hal_embassy::InterruptExecutor;
|
||||
@ -25,7 +27,7 @@ use static_cell::StaticCell;
|
||||
|
||||
#[allow(unused)] // compile test
|
||||
fn baremetal_preempt_can_be_initialized_with_any_timer(timer: esp_hal::timer::AnyTimer<'static>) {
|
||||
esp_preempt::init(timer);
|
||||
esp_preempt::start(timer);
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
@ -34,7 +36,7 @@ async fn try_init(
|
||||
timer: TIMG0<'static>,
|
||||
) {
|
||||
let timg0 = TimerGroup::new(timer);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
match esp_radio::init() {
|
||||
Ok(_) => signal.signal(None),
|
||||
@ -45,8 +47,6 @@ async fn try_init(
|
||||
#[cfg(test)]
|
||||
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())]
|
||||
mod tests {
|
||||
use defmt::info;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[init]
|
||||
@ -71,7 +71,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_init_fails_cs(peripherals: Peripherals) {
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let init = critical_section::with(|_| esp_radio::init());
|
||||
|
||||
@ -81,7 +81,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_init_fails_interrupt_free(peripherals: Peripherals) {
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let init = interrupt_free(|| esp_radio::init());
|
||||
|
||||
@ -115,7 +115,7 @@ mod tests {
|
||||
#[cfg(soc_has_wifi)]
|
||||
fn test_wifi_can_be_initialized(mut p: Peripherals) {
|
||||
let timg0 = TimerGroup::new(p.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl =
|
||||
&*mk_static!(esp_radio::Controller<'static>, esp_radio::init().unwrap());
|
||||
@ -128,6 +128,19 @@ mod tests {
|
||||
let _wifi = esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_esp_preempt_sleep_wakes_up(p: Peripherals) {
|
||||
use esp_radio_preempt_driver as preempt;
|
||||
let timg0 = TimerGroup::new(p.TIMG0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
preempt::usleep(10_000);
|
||||
|
||||
assert!(now.elapsed() >= Duration::from_millis(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_esp_preempt_priority_inheritance(p: Peripherals) {
|
||||
use core::ffi::c_void;
|
||||
@ -137,8 +150,7 @@ mod tests {
|
||||
use preempt::semaphore::{SemaphoreHandle, SemaphoreKind};
|
||||
|
||||
let timg0 = TimerGroup::new(p.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
preempt::enable();
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
// We need three tasks to test priority inheritance:
|
||||
// - A high priority task that will attempt to acquire the mutex.
|
||||
@ -235,6 +247,5 @@ mod tests {
|
||||
|
||||
info!("Low: wait for tasks to finish");
|
||||
test_context.ready_semaphore.take(None);
|
||||
preempt::disable();
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
let server_address: Ipv4Addr = HOST_IP.parse().expect("Invalid HOST_IP address");
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
||||
|
||||
|
@ -62,7 +62,7 @@ fn main() -> ! {
|
||||
let server_address: Ipv4Addr = HOST_IP.parse().expect("Invalid HOST_IP address");
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
@ -36,7 +36,7 @@ fn main() -> ! {
|
||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
esp_preempt::init(timg0.timer0);
|
||||
esp_preempt::start(timg0.timer0);
|
||||
|
||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user