use core::{
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
};
use xtensa_lx::set_stack_pointer;
use crate::Cpu;
#[repr(C, align(16))]
pub struct Stack<const SIZE: usize> {
pub mem: MaybeUninit<[u8; SIZE]>,
}
#[allow(clippy::len_without_is_empty)]
impl<const SIZE: usize> Stack<SIZE> {
pub const fn new() -> Stack<SIZE> {
::core::assert!(SIZE % 16 == 0);
Stack {
mem: MaybeUninit::uninit(),
}
}
pub const fn len(&self) -> usize {
SIZE
}
pub fn bottom(&mut self) -> *mut u32 {
self.mem.as_mut_ptr() as *mut u32
}
pub fn top(&mut self) -> *mut u32 {
unsafe { self.bottom().add(SIZE / 4) }
}
}
static mut START_CORE1_FUNCTION: Option<*mut ()> = None;
static mut APP_CORE_STACK_TOP: Option<*mut u32> = None;
#[must_use]
pub struct AppCoreGuard<'a> {
phantom: PhantomData<&'a ()>,
}
impl<'a> Drop for AppCoreGuard<'a> {
fn drop(&mut self) {
unsafe {
internal_park_core(Cpu::AppCpu);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
CoreAlreadyRunning,
}
pub struct CpuControl {
_cpu_control: crate::system::CpuControl,
}
unsafe fn internal_park_core(core: Cpu) {
let rtc_control = crate::peripherals::RTC_CNTL::PTR;
let rtc_control = &*rtc_control;
match core {
Cpu::ProCpu => {
rtc_control
.sw_cpu_stall()
.modify(|_, w| w.sw_stall_procpu_c1().bits(0x21));
rtc_control
.options0()
.modify(|_, w| w.sw_stall_procpu_c0().bits(0x02));
}
Cpu::AppCpu => {
rtc_control
.sw_cpu_stall()
.modify(|_, w| w.sw_stall_appcpu_c1().bits(0x21));
rtc_control
.options0()
.modify(|_, w| w.sw_stall_appcpu_c0().bits(0x02));
}
}
}
impl CpuControl {
pub fn new(cpu_control: crate::system::CpuControl) -> CpuControl {
CpuControl {
_cpu_control: cpu_control,
}
}
pub unsafe fn park_core(&mut self, core: Cpu) {
internal_park_core(core);
}
pub fn unpark_core(&mut self, core: Cpu) {
let rtc_control = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
match core {
Cpu::ProCpu => {
rtc_control
.sw_cpu_stall()
.modify(|_, w| unsafe { w.sw_stall_procpu_c1().bits(0) });
rtc_control
.options0()
.modify(|_, w| unsafe { w.sw_stall_procpu_c0().bits(0) });
}
Cpu::AppCpu => {
rtc_control
.sw_cpu_stall()
.modify(|_, w| unsafe { w.sw_stall_appcpu_c1().bits(0) });
rtc_control
.options0()
.modify(|_, w| unsafe { w.sw_stall_appcpu_c0().bits(0) });
}
}
}
unsafe fn start_core1_init<F>() -> !
where
F: FnOnce(),
{
xtensa_lx::interrupt::set_mask(0);
xtensa_lx::timer::set_ccompare0(0);
xtensa_lx::timer::set_ccompare1(0);
xtensa_lx::timer::set_ccompare2(0);
set_stack_pointer(unsafe { unwrap!(APP_CORE_STACK_TOP) });
extern "C" {
static mut _init_start: u32;
}
unsafe {
let base = core::ptr::addr_of!(_init_start);
core::arch::asm!("wsr.vecbase {0}", in(reg) base, options(nostack));
}
match START_CORE1_FUNCTION.take() {
Some(entry) => {
let entry = unsafe { ManuallyDrop::take(&mut *entry.cast::<ManuallyDrop<F>>()) };
entry();
loop {
unsafe { internal_park_core(crate::get_core()) };
}
}
None => panic!("No start function set"),
}
}
pub fn start_app_core<'a, const SIZE: usize, F>(
&mut self,
stack: &'static mut Stack<SIZE>,
entry: F,
) -> Result<AppCoreGuard<'a>, Error>
where
F: FnOnce(),
F: Send + 'a,
{
let system_control = crate::peripherals::SYSTEM::PTR;
let system_control = unsafe { &*system_control };
if !xtensa_lx::is_debugger_attached()
&& system_control
.core_1_control_0()
.read()
.control_core_1_clkgate_en()
.bit_is_set()
{
return Err(Error::CoreAlreadyRunning);
}
let entry = ManuallyDrop::new(entry);
unsafe {
let stack_bottom = stack.bottom().cast::<u8>();
let align_offset = stack_bottom.align_offset(core::mem::align_of::<F>());
let entry_dst = stack_bottom.add(align_offset).cast::<ManuallyDrop<F>>();
entry_dst.write(entry);
let entry_fn = entry_dst.cast::<()>();
START_CORE1_FUNCTION = Some(entry_fn);
APP_CORE_STACK_TOP = Some(stack.top());
}
const ETS_SET_APPCPU_BOOT_ADDR: usize = 0x40000720;
unsafe {
let ets_set_appcpu_boot_addr: unsafe extern "C" fn(u32) =
core::mem::transmute(ETS_SET_APPCPU_BOOT_ADDR);
ets_set_appcpu_boot_addr(Self::start_core1_init::<F> as *const u32 as u32);
};
system_control
.core_1_control_0()
.modify(|_, w| w.control_core_1_clkgate_en().set_bit());
system_control
.core_1_control_0()
.modify(|_, w| w.control_core_1_runstall().clear_bit());
system_control
.core_1_control_0()
.modify(|_, w| w.control_core_1_reseting().set_bit());
system_control
.core_1_control_0()
.modify(|_, w| w.control_core_1_reseting().clear_bit());
self.unpark_core(Cpu::AppCpu);
Ok(AppCoreGuard {
phantom: PhantomData,
})
}
}