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 = crate::peripherals::RTC_CNTL::PTR;
let rtc_control = unsafe { &*rtc_control };
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) });
}
}
}
fn flush_cache(&mut self, core: Cpu) {
let dport_control = crate::peripherals::DPORT::PTR;
let dport_control = unsafe { &*dport_control };
match core {
Cpu::ProCpu => {
dport_control
.pro_cache_ctrl()
.modify(|_, w| w.pro_cache_flush_ena().clear_bit());
dport_control
.pro_cache_ctrl()
.modify(|_, w| w.pro_cache_flush_ena().set_bit());
while dport_control
.pro_cache_ctrl()
.read()
.pro_cache_flush_done()
.bit_is_clear()
{}
dport_control
.pro_cache_ctrl()
.modify(|_, w| w.pro_cache_flush_ena().clear_bit());
}
Cpu::AppCpu => {
dport_control
.app_cache_ctrl()
.modify(|_, w| w.app_cache_flush_ena().clear_bit());
dport_control
.app_cache_ctrl()
.modify(|_, w| w.app_cache_flush_ena().set_bit());
while dport_control
.app_cache_ctrl()
.read()
.app_cache_flush_done()
.bit_is_clear()
{}
dport_control
.app_cache_ctrl()
.modify(|_, w| w.app_cache_flush_ena().clear_bit());
}
};
}
fn enable_cache(&mut self, core: Cpu) {
let spi0 = unsafe { &*crate::peripherals::SPI0::ptr() };
let dport_control = unsafe { &*crate::peripherals::DPORT::ptr() };
match core {
Cpu::ProCpu => {
spi0.cache_fctrl().modify(|_, w| w.cache_req_en().set_bit());
dport_control
.pro_cache_ctrl()
.modify(|_, w| w.pro_cache_enable().set_bit());
}
Cpu::AppCpu => {
spi0.cache_fctrl().modify(|_, w| w.cache_req_en().set_bit());
dport_control
.app_cache_ctrl()
.modify(|_, w| w.app_cache_enable().set_bit());
}
};
}
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 dport_control = crate::peripherals::DPORT::PTR;
let dport_control = unsafe { &*dport_control };
if !xtensa_lx::is_debugger_attached()
&& dport_control
.appcpu_ctrl_b()
.read()
.appcpu_clkgate_en()
.bit_is_set()
{
return Err(Error::CoreAlreadyRunning);
}
self.flush_cache(Cpu::AppCpu);
self.enable_cache(Cpu::AppCpu);
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());
}
dport_control.appcpu_ctrl_d().write(|w| unsafe {
w.appcpu_boot_addr()
.bits(Self::start_core1_init::<F> as *const u32 as u32)
});
dport_control
.appcpu_ctrl_b()
.modify(|_, w| w.appcpu_clkgate_en().set_bit());
dport_control
.appcpu_ctrl_c()
.modify(|_, w| w.appcpu_runstall().clear_bit());
dport_control
.appcpu_ctrl_a()
.modify(|_, w| w.appcpu_resetting().set_bit());
dport_control
.appcpu_ctrl_a()
.modify(|_, w| w.appcpu_resetting().clear_bit());
self.unpark_core(Cpu::AppCpu);
Ok(AppCoreGuard {
phantom: PhantomData,
})
}
}