mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-02 06:40:47 +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 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
|
//! ```rust, no_run
|
||||||
#![doc = esp_hal::before_snippet!()]
|
#![doc = esp_hal::before_snippet!()]
|
||||||
//! use esp_hal::timer::timg::TimerGroup;
|
//! use esp_hal::timer::timg::TimerGroup;
|
||||||
//!
|
//!
|
||||||
//! let timg0 = TimerGroup::new(peripherals.TIMG0);
|
//! let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
//! esp_preempt::init(timg0.timer0);
|
//! esp_preempt::start(timg0.timer0);
|
||||||
//!
|
//!
|
||||||
//! // You can now start esp-radio:
|
//! // You can now start esp-radio:
|
||||||
//! // let esp_radio_controller = esp_radio::init().unwrap();
|
//! // let esp_radio_controller = esp_radio::init().unwrap();
|
||||||
@ -35,7 +35,6 @@ mod wait_queue;
|
|||||||
pub(crate) use esp_alloc::InternalMemory;
|
pub(crate) use esp_alloc::InternalMemory;
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
Blocking,
|
Blocking,
|
||||||
system::Cpu,
|
|
||||||
timer::{AnyTimer, OneShotTimer},
|
timer::{AnyTimer, OneShotTimer},
|
||||||
};
|
};
|
||||||
pub(crate) use scheduler::SCHEDULER;
|
pub(crate) use scheduler::SCHEDULER;
|
||||||
@ -102,7 +101,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes the scheduler.
|
/// Starts the scheduler.
|
||||||
///
|
///
|
||||||
/// # The `timer` argument
|
/// # The `timer` argument
|
||||||
///
|
///
|
||||||
@ -116,12 +115,19 @@ where
|
|||||||
/// For an example, see the [crate-level documentation][self].
|
/// For an example, see the [crate-level documentation][self].
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
multi_core,
|
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.with(move |scheduler| {
|
||||||
scheduler.time_driver = Some(TimeDriver::new(timer.timer()));
|
scheduler.setup(TimeDriver::new(timer.timer()));
|
||||||
scheduler.runs_on = Cpu::current();
|
|
||||||
|
// 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,
|
TaskPtr,
|
||||||
},
|
},
|
||||||
timer::TimeDriver,
|
timer::TimeDriver,
|
||||||
timer_queue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
@ -69,6 +68,7 @@ pub(crate) struct SchedulerState {
|
|||||||
|
|
||||||
/// Pointer to the current task.
|
/// Pointer to the current task.
|
||||||
pub(crate) current_task: Option<TaskPtr>,
|
pub(crate) current_task: Option<TaskPtr>,
|
||||||
|
idle_context: CpuContext,
|
||||||
|
|
||||||
/// A list of all allocated tasks
|
/// A list of all allocated tasks
|
||||||
pub(crate) all_tasks: TaskList<TaskAllocListElement>,
|
pub(crate) all_tasks: TaskList<TaskAllocListElement>,
|
||||||
@ -91,6 +91,7 @@ impl SchedulerState {
|
|||||||
Self {
|
Self {
|
||||||
runs_on: Cpu::ProCpu,
|
runs_on: Cpu::ProCpu,
|
||||||
current_task: None,
|
current_task: None,
|
||||||
|
idle_context: CpuContext::new(),
|
||||||
all_tasks: TaskList::new(),
|
all_tasks: TaskList::new(),
|
||||||
run_queue: RunQueue::new(),
|
run_queue: RunQueue::new(),
|
||||||
to_delete: TaskList::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) {
|
fn delete_marked_tasks(&mut self) {
|
||||||
while let Some(to_delete) = self.to_delete.pop() {
|
while let Some(to_delete) = self.to_delete.pop() {
|
||||||
trace!("delete_marked_tasks {:?}", to_delete);
|
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)) {
|
fn run_scheduler(&mut self, task_switch: impl FnOnce(*mut CpuContext, *mut CpuContext)) {
|
||||||
let mut priority = {
|
let mut priority = if let Some(current_task) = self.current_task {
|
||||||
let current_task = unsafe { unwrap!(self.current_task).as_ref() };
|
let current_task = unsafe { current_task.as_ref() };
|
||||||
current_task.ensure_no_stack_overflow();
|
current_task.ensure_no_stack_overflow();
|
||||||
current_task.priority
|
Some(current_task.priority)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
self.delete_marked_tasks();
|
self.delete_marked_tasks();
|
||||||
@ -146,20 +179,8 @@ impl SchedulerState {
|
|||||||
self.run_queue.mark_task_ready(current_task);
|
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
|
let next_task = self.run_queue.pop();
|
||||||
// can't just WFI from an interrupt handler. We should create an idle task that WFIs for us,
|
if next_task != self.current_task {
|
||||||
// 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
|
|
||||||
);
|
|
||||||
|
|
||||||
trace!("Switching task {:?} -> {:?}", self.current_task, next_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
|
// If the current task is deleted, we can skip saving its context. We signal this by
|
||||||
@ -169,19 +190,48 @@ impl SchedulerState {
|
|||||||
} else {
|
} else {
|
||||||
core::ptr::null_mut()
|
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);
|
task_switch(current_context, next_context);
|
||||||
|
|
||||||
self.current_task = Some(next_task);
|
// If we went to idle, this will be None and we won't mess up the main task's stack.
|
||||||
priority = next_task.priority(&mut self.run_queue);
|
self.current_task = next_task;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO maybe we don't need to do this every time?
|
// 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
|
// 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
|
// is empty, the current task is the only one running at its priority level. In this
|
||||||
// case, we don't need time slicing.
|
// 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);
|
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);
|
self.remove_from_all_queues(to_delete);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let task = Box::from_raw_in(to_delete.as_ptr(), InternalMemory);
|
if to_delete.as_ref().heap_allocated {
|
||||||
core::mem::drop(task);
|
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,
|
task_stack_size: usize,
|
||||||
priority: u32,
|
priority: u32,
|
||||||
) -> TaskPtr {
|
) -> TaskPtr {
|
||||||
let task = Box::new_in(
|
self.with(|state| state.create_task(task, param, task_stack_size, priority as usize))
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn sleep_until(&self, wake_at: Instant) -> bool {
|
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) {
|
fn yield_task(&self) {
|
||||||
task::yield_task();
|
task::yield_task();
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,13 @@ use allocator_api2::boxed::Box;
|
|||||||
pub(crate) use arch_specific::*;
|
pub(crate) use arch_specific::*;
|
||||||
use esp_radio_preempt_driver::semaphore::{SemaphoreHandle, SemaphorePtr};
|
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)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -195,7 +201,7 @@ pub(crate) struct Task {
|
|||||||
pub cpu_context: CpuContext,
|
pub cpu_context: CpuContext,
|
||||||
pub thread_semaphore: Option<SemaphorePtr>,
|
pub thread_semaphore: Option<SemaphorePtr>,
|
||||||
pub state: TaskState,
|
pub state: TaskState,
|
||||||
pub _allocated_stack: Box<[MaybeUninit<u32>], InternalMemory>,
|
pub _allocated_stack: *mut [MaybeUninit<u32>],
|
||||||
pub priority: usize,
|
pub priority: usize,
|
||||||
|
|
||||||
pub wakeup_at: u64,
|
pub wakeup_at: u64,
|
||||||
@ -215,6 +221,9 @@ pub(crate) struct Task {
|
|||||||
|
|
||||||
/// The list of tasks scheduled for deletion
|
/// The list of tasks scheduled for deletion
|
||||||
pub delete_list_item: TaskListItem,
|
pub delete_list_item: TaskListItem,
|
||||||
|
|
||||||
|
/// Whether the task was allocated on the heap.
|
||||||
|
pub(crate) heap_allocated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const STACK_CANARY: u32 = 0xDEEDBAAD;
|
const STACK_CANARY: u32 = 0xDEEDBAAD;
|
||||||
@ -233,11 +242,11 @@ impl Task {
|
|||||||
|
|
||||||
let task_stack_size_words = task_stack_size / 4;
|
let task_stack_size_words = task_stack_size / 4;
|
||||||
let mut stack = Box::<[u32], _>::new_uninit_slice_in(task_stack_size_words, InternalMemory);
|
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);
|
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 {
|
Task {
|
||||||
cpu_context: new_task_context(task_fn, param, stack_top),
|
cpu_context: new_task_context(task_fn, param, stack_top),
|
||||||
thread_semaphore: None,
|
thread_semaphore: None,
|
||||||
@ -252,16 +261,21 @@ impl Task {
|
|||||||
ready_queue_item: TaskListItem::None,
|
ready_queue_item: TaskListItem::None,
|
||||||
timer_queue_item: TaskListItem::None,
|
timer_queue_item: TaskListItem::None,
|
||||||
delete_list_item: TaskListItem::None,
|
delete_list_item: TaskListItem::None,
|
||||||
|
|
||||||
|
heap_allocated: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ensure_no_stack_overflow(&self) {
|
pub(crate) fn ensure_no_stack_overflow(&self) {
|
||||||
|
// TODO: fix this for main task
|
||||||
if self._allocated_stack.is_empty() {
|
if self._allocated_stack.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
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_CANARY,
|
||||||
"Stack overflow detected in {:?}",
|
"Stack overflow detected in {:?}",
|
||||||
self as *const Task
|
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.
|
||||||
// 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
|
||||||
let task = Box::new_in(
|
// don't deallocate this.
|
||||||
Task {
|
pub(crate) static mut MAIN_TASK: Task = Task {
|
||||||
cpu_context: CpuContext::default(),
|
cpu_context: CpuContext::new(),
|
||||||
thread_semaphore: None,
|
thread_semaphore: None,
|
||||||
state: TaskState::Ready,
|
state: TaskState::Ready,
|
||||||
_allocated_stack: Box::<[u32], _>::new_uninit_slice_in(0, InternalMemory),
|
_allocated_stack: core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0),
|
||||||
current_queue: None,
|
current_queue: None,
|
||||||
priority: 1,
|
priority: 0,
|
||||||
|
|
||||||
wakeup_at: 0,
|
wakeup_at: 0,
|
||||||
|
|
||||||
alloc_list_item: TaskListItem::None,
|
alloc_list_item: TaskListItem::None,
|
||||||
ready_queue_item: TaskListItem::None,
|
ready_queue_item: TaskListItem::None,
|
||||||
timer_queue_item: TaskListItem::None,
|
timer_queue_item: TaskListItem::None,
|
||||||
delete_list_item: TaskListItem::None,
|
delete_list_item: TaskListItem::None,
|
||||||
},
|
|
||||||
InternalMemory,
|
heap_allocated: false,
|
||||||
);
|
};
|
||||||
let main_task_ptr = NonNull::from(Box::leak(task));
|
|
||||||
|
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);
|
debug!("Main task created: {:?}", main_task_ptr);
|
||||||
|
|
||||||
SCHEDULER.with(|state| {
|
unsafe {
|
||||||
debug_assert!(
|
let main_task = main_task_ptr.as_mut();
|
||||||
state.current_task.is_none(),
|
|
||||||
"Tried to allocate main task multiple times"
|
|
||||||
);
|
|
||||||
|
|
||||||
// The main task is already running, no need to add it to the ready queue.
|
// Reset main task properties. The rest should be cleared when the task is deleted.
|
||||||
state.all_tasks.push(main_task_ptr);
|
main_task.priority = 0;
|
||||||
state.current_task = Some(main_task_ptr);
|
main_task.state = TaskState::Ready;
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn delete_all_tasks() {
|
debug_assert!(
|
||||||
trace!("delete_all_tasks");
|
scheduler.current_task.is_none(),
|
||||||
let mut all_tasks = SCHEDULER.with(|state| {
|
"Tried to allocate main task multiple times"
|
||||||
// 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();
|
|
||||||
|
|
||||||
// Clear the current task.
|
// The main task is already running, no need to add it to the ready queue.
|
||||||
state.current_task = None;
|
scheduler.all_tasks.push(main_task_ptr);
|
||||||
|
scheduler.current_task = Some(main_task_ptr);
|
||||||
// Take the allocation list
|
scheduler.run_queue.mark_task_ready(main_task_ptr);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn with_current_task<R>(mut cb: impl FnMut(&mut Task) -> R) -> R {
|
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 core::ffi::c_void;
|
||||||
|
|
||||||
use esp_hal::{
|
use esp_hal::{interrupt::software::SoftwareInterrupt, riscv::register};
|
||||||
interrupt::{self, software::SoftwareInterrupt},
|
|
||||||
peripherals::Interrupt,
|
|
||||||
riscv::register,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::SCHEDULER;
|
use crate::SCHEDULER;
|
||||||
|
|
||||||
@ -102,6 +98,25 @@ pub struct CpuContext {
|
|||||||
pub mstatus: usize,
|
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(
|
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,
|
||||||
@ -286,10 +301,6 @@ pub(crate) fn setup_multitasking() {
|
|||||||
unsafe { SoftwareInterrupt::<2>::steal() }.set_interrupt_handler(swint2_handler);
|
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]
|
#[esp_hal::ram]
|
||||||
extern "C" fn swint2_handler() {
|
extern "C" fn swint2_handler() {
|
||||||
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
let swi = unsafe { SoftwareInterrupt::<2>::steal() };
|
||||||
|
@ -5,6 +5,27 @@ use esp_hal::{xtensa_lx, xtensa_lx_rt};
|
|||||||
|
|
||||||
use crate::SCHEDULER;
|
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(
|
pub(crate) fn new_task_context(
|
||||||
task_fn: extern "C" fn(*mut c_void),
|
task_fn: extern "C" fn(*mut c_void),
|
||||||
param: *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)]
|
#[allow(non_snake_case)]
|
||||||
#[esp_hal::ram]
|
#[esp_hal::ram]
|
||||||
#[cfg_attr(not(esp32), unsafe(export_name = "Software0"))]
|
#[cfg_attr(not(esp32), unsafe(export_name = "Software0"))]
|
||||||
|
@ -86,11 +86,6 @@ impl TimeDriver {
|
|||||||
self.timer.listen();
|
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)) {
|
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);
|
let mut timer_queue = core::mem::take(&mut self.timer_queue);
|
||||||
|
|
||||||
|
@ -323,12 +323,6 @@ impl TimerImplementation for Timer {
|
|||||||
|
|
||||||
register_timer_implementation!(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
|
/// Entry point for the timer task responsible for handling scheduled timer
|
||||||
/// events.
|
/// events.
|
||||||
///
|
///
|
||||||
|
@ -34,8 +34,6 @@ use crate::semaphore::SemaphorePtr;
|
|||||||
|
|
||||||
unsafe extern "Rust" {
|
unsafe extern "Rust" {
|
||||||
fn esp_preempt_initialized() -> bool;
|
fn esp_preempt_initialized() -> bool;
|
||||||
fn esp_preempt_enable();
|
|
||||||
fn esp_preempt_disable();
|
|
||||||
fn esp_preempt_yield_task();
|
fn esp_preempt_yield_task();
|
||||||
fn esp_preempt_yield_task_from_isr();
|
fn esp_preempt_yield_task_from_isr();
|
||||||
fn esp_preempt_current_task() -> *mut c_void;
|
fn esp_preempt_current_task() -> *mut c_void;
|
||||||
@ -68,18 +66,6 @@ macro_rules! scheduler_impl {
|
|||||||
<$t as $crate::Scheduler>::initialized(&$name)
|
<$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)]
|
#[unsafe(no_mangle)]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn esp_preempt_yield_task() {
|
fn esp_preempt_yield_task() {
|
||||||
@ -166,14 +152,6 @@ macro_rules! scheduler_impl {
|
|||||||
/// unimplemented!()
|
/// unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn enable(&self) {
|
|
||||||
/// unimplemented!()
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn disable(&self) {
|
|
||||||
/// unimplemented!()
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn yield_task(&self) {
|
/// fn yield_task(&self) {
|
||||||
/// unimplemented!()
|
/// unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
@ -225,12 +203,6 @@ pub trait Scheduler: Send + Sync + 'static {
|
|||||||
/// up.
|
/// up.
|
||||||
fn initialized(&self) -> bool;
|
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.
|
/// This function is called by `esp_radio` to yield control to another task.
|
||||||
fn yield_task(&self);
|
fn yield_task(&self);
|
||||||
|
|
||||||
@ -287,18 +259,6 @@ pub fn initialized() -> bool {
|
|||||||
unsafe { esp_preempt_initialized() }
|
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.
|
/// Yields control to another task.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn yield_task() {
|
pub fn yield_task() {
|
||||||
|
@ -11,7 +11,7 @@ Furthermore, `esp_wifi::init` no longer requires `RNG` or a timer.
|
|||||||
|
|
||||||
```diff
|
```diff
|
||||||
-let esp_wifi_ctrl = esp_wifi::init(timg0.timer0, Rng::new()).unwrap();
|
-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();
|
+let esp_wifi_ctrl = esp_radio::init().unwrap();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -229,9 +229,6 @@ impl Drop for Controller<'_> {
|
|||||||
|
|
||||||
shutdown_radio_isr();
|
shutdown_radio_isr();
|
||||||
|
|
||||||
// This shuts down the task switcher and timer tick interrupt.
|
|
||||||
preempt::disable();
|
|
||||||
|
|
||||||
#[cfg(esp32)]
|
#[cfg(esp32)]
|
||||||
// Allow using `ADC2` again
|
// Allow using `ADC2` again
|
||||||
release_adc2(unsafe { esp_hal::Internal::conjure() });
|
release_adc2(unsafe { esp_hal::Internal::conjure() });
|
||||||
@ -271,7 +268,7 @@ impl Drop for Controller<'_> {
|
|||||||
/// use esp_hal::timer::timg::TimerGroup;
|
/// use esp_hal::timer::timg::TimerGroup;
|
||||||
///
|
///
|
||||||
/// let timg0 = TimerGroup::new(peripherals.TIMG0);
|
/// let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
/// esp_preempt::init(timg0.timer0);
|
/// esp_preempt::start(timg0.timer0);
|
||||||
///
|
///
|
||||||
/// // You can now start esp-radio:
|
/// // You can now start esp-radio:
|
||||||
/// let esp_radio_controller = esp_radio::init().unwrap();
|
/// let esp_radio_controller = esp_radio::init().unwrap();
|
||||||
@ -303,9 +300,6 @@ pub fn init<'d>() -> Result<Controller<'d>, InitializationError> {
|
|||||||
|
|
||||||
setup_radio_isr();
|
setup_radio_isr();
|
||||||
|
|
||||||
// This initializes the task switcher
|
|
||||||
preempt::enable();
|
|
||||||
|
|
||||||
wifi_set_log_verbose();
|
wifi_set_log_verbose();
|
||||||
init_radio_clocks();
|
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()));
|
let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
|
||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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();
|
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
|
||||||
let radio = RADIO.init(esp_radio::init().unwrap());
|
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()));
|
let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
|
||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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();
|
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
|
||||||
let radio = RADIO.init(esp_radio::init().unwrap());
|
let radio = RADIO.init(esp_radio::init().unwrap());
|
||||||
|
@ -40,7 +40,7 @@ async fn main(_spawner: Spawner) -> ! {
|
|||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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());
|
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);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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());
|
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);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ fn main() -> ! {
|
|||||||
let delay = Delay::new();
|
let delay = Delay::new();
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ fn main() -> ! {
|
|||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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.
|
// Set event handlers for wifi before init to avoid missing any.
|
||||||
let mut connections = 0u32;
|
let mut connections = 0u32;
|
||||||
|
@ -47,7 +47,7 @@ fn main() -> ! {
|
|||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ fn main() -> ! {
|
|||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ async fn main(spawner: Spawner) -> ! {
|
|||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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());
|
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);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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());
|
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);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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());
|
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);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ async fn main(spawner: Spawner) -> ! {
|
|||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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());
|
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);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
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 p = esp_hal::init(esp_hal::Config::default());
|
||||||
|
|
||||||
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
|
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
{
|
{
|
||||||
let _init = esp_radio::init().unwrap();
|
let _init = esp_radio::init().unwrap();
|
||||||
@ -58,7 +58,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_controller_comms(peripherals: Peripherals) {
|
fn test_controller_comms(peripherals: Peripherals) {
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
let init = esp_radio::init().unwrap();
|
let init = esp_radio::init().unwrap();
|
||||||
|
|
||||||
let mut connector = BleConnector::new(&init, peripherals.BT);
|
let mut connector = BleConnector::new(&init, peripherals.BT);
|
||||||
@ -108,7 +108,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_dropping_controller_during_reset(peripherals: Peripherals) {
|
fn test_dropping_controller_during_reset(peripherals: Peripherals) {
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
let init = esp_radio::init().unwrap();
|
let init = esp_radio::init().unwrap();
|
||||||
|
|
||||||
let mut connector = BleConnector::new(&init, peripherals.BT);
|
let mut connector = BleConnector::new(&init, peripherals.BT);
|
||||||
|
@ -94,7 +94,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fpu_stays_enabled_with_wifi(peripherals: Peripherals) {
|
fn fpu_stays_enabled_with_wifi(peripherals: Peripherals) {
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
let _init = esp_radio::init().unwrap();
|
let _init = esp_radio::init().unwrap();
|
||||||
|
|
||||||
let mut sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
let mut sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||||
@ -138,7 +138,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
let _init = esp_radio::init().unwrap();
|
let _init = esp_radio::init().unwrap();
|
||||||
|
|
||||||
let mut sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
let mut sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
|
||||||
#[cfg(target_arch = "riscv32")]
|
#[cfg(target_arch = "riscv32")]
|
||||||
use esp_hal::riscv::interrupt::free as interrupt_free;
|
use esp_hal::riscv::interrupt::free as interrupt_free;
|
||||||
@ -16,6 +17,7 @@ use esp_hal::{
|
|||||||
clock::CpuClock,
|
clock::CpuClock,
|
||||||
interrupt::{Priority, software::SoftwareInterruptControl},
|
interrupt::{Priority, software::SoftwareInterruptControl},
|
||||||
peripherals::{Peripherals, TIMG0},
|
peripherals::{Peripherals, TIMG0},
|
||||||
|
time::{Duration, Instant},
|
||||||
timer::timg::TimerGroup,
|
timer::timg::TimerGroup,
|
||||||
};
|
};
|
||||||
use esp_hal_embassy::InterruptExecutor;
|
use esp_hal_embassy::InterruptExecutor;
|
||||||
@ -25,7 +27,7 @@ use static_cell::StaticCell;
|
|||||||
|
|
||||||
#[allow(unused)] // compile test
|
#[allow(unused)] // compile test
|
||||||
fn baremetal_preempt_can_be_initialized_with_any_timer(timer: esp_hal::timer::AnyTimer<'static>) {
|
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]
|
#[embassy_executor::task]
|
||||||
@ -34,7 +36,7 @@ async fn try_init(
|
|||||||
timer: TIMG0<'static>,
|
timer: TIMG0<'static>,
|
||||||
) {
|
) {
|
||||||
let timg0 = TimerGroup::new(timer);
|
let timg0 = TimerGroup::new(timer);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
match esp_radio::init() {
|
match esp_radio::init() {
|
||||||
Ok(_) => signal.signal(None),
|
Ok(_) => signal.signal(None),
|
||||||
@ -45,8 +47,6 @@ async fn try_init(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())]
|
#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())]
|
||||||
mod tests {
|
mod tests {
|
||||||
use defmt::info;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
@ -71,7 +71,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_init_fails_cs(peripherals: Peripherals) {
|
fn test_init_fails_cs(peripherals: Peripherals) {
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let init = critical_section::with(|_| esp_radio::init());
|
let init = critical_section::with(|_| esp_radio::init());
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_init_fails_interrupt_free(peripherals: Peripherals) {
|
fn test_init_fails_interrupt_free(peripherals: Peripherals) {
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let init = interrupt_free(|| esp_radio::init());
|
let init = interrupt_free(|| esp_radio::init());
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ mod tests {
|
|||||||
#[cfg(soc_has_wifi)]
|
#[cfg(soc_has_wifi)]
|
||||||
fn test_wifi_can_be_initialized(mut p: Peripherals) {
|
fn test_wifi_can_be_initialized(mut p: Peripherals) {
|
||||||
let timg0 = TimerGroup::new(p.TIMG0);
|
let timg0 = TimerGroup::new(p.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl =
|
let esp_radio_ctrl =
|
||||||
&*mk_static!(esp_radio::Controller<'static>, esp_radio::init().unwrap());
|
&*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();
|
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]
|
#[test]
|
||||||
fn test_esp_preempt_priority_inheritance(p: Peripherals) {
|
fn test_esp_preempt_priority_inheritance(p: Peripherals) {
|
||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
||||||
@ -137,8 +150,7 @@ mod tests {
|
|||||||
use preempt::semaphore::{SemaphoreHandle, SemaphoreKind};
|
use preempt::semaphore::{SemaphoreHandle, SemaphoreKind};
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(p.TIMG0);
|
let timg0 = TimerGroup::new(p.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
preempt::enable();
|
|
||||||
|
|
||||||
// We need three tasks to test priority inheritance:
|
// We need three tasks to test priority inheritance:
|
||||||
// - A high priority task that will attempt to acquire the mutex.
|
// - A high priority task that will attempt to acquire the mutex.
|
||||||
@ -235,6 +247,5 @@ mod tests {
|
|||||||
|
|
||||||
info!("Low: wait for tasks to finish");
|
info!("Low: wait for tasks to finish");
|
||||||
test_context.ready_semaphore.take(None);
|
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 server_address: Ipv4Addr = HOST_IP.parse().expect("Invalid HOST_IP address");
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
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());
|
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 server_address: Ipv4Addr = HOST_IP.parse().expect("Invalid HOST_IP address");
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ fn main() -> ! {
|
|||||||
esp_alloc::heap_allocator!(size: 72 * 1024);
|
esp_alloc::heap_allocator!(size: 72 * 1024);
|
||||||
|
|
||||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_preempt::init(timg0.timer0);
|
esp_preempt::start(timg0.timer0);
|
||||||
|
|
||||||
let esp_radio_ctrl = esp_radio::init().unwrap();
|
let esp_radio_ctrl = esp_radio::init().unwrap();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user