Skip to main content

esp_hal/soc/esp32s31/
cpu_control.rs

1#[cfg(feature = "unstable")]
2use crate::system::multi_core;
3use crate::{
4    peripherals::{HP_SYS, HP_SYS_CLKRST, LP_AON_CLK_RST, PMU},
5    system::Cpu,
6};
7
8pub(crate) unsafe fn internal_park_core(core: Cpu, park: bool) {
9    // 0x86 = stalled, 0xFF = running (matches IDF cpu_utility_ll.h).
10    let code: u8 = if park { 0x86 } else { 0xFF };
11    PMU::regs().cpu_stall_sw().modify(|_, w| unsafe {
12        match core {
13            Cpu::ProCpu => w.hpcore0_sw_stall_code().bits(code),
14            Cpu::AppCpu => w.hpcore1_sw_stall_code().bits(code),
15        }
16    });
17
18    // IDF waits for the core-stalled status to clear after unstalling.
19    if !park {
20        let status = HP_SYS::regs().cpu_corestalled_st();
21        match core {
22            Cpu::ProCpu => while status.read().reg_core0_corestalled_st().bit_is_set() {},
23            Cpu::AppCpu => while status.read().reg_core1_corestalled_st().bit_is_set() {},
24        }
25    }
26}
27
28#[instability::unstable]
29pub fn is_running(core: Cpu) -> bool {
30    let stall = PMU::regs().cpu_stall_sw().read();
31    let code = match core {
32        Cpu::ProCpu => stall.hpcore0_sw_stall_code().bits(),
33        Cpu::AppCpu => stall.hpcore1_sw_stall_code().bits(),
34    };
35    if code == 0x86 {
36        return false;
37    }
38
39    match core {
40        Cpu::ProCpu => true,
41        Cpu::AppCpu => {
42            let control = HP_SYS_CLKRST::regs().hpcore1_ctrl0().read();
43            control.core1_cpu_clk_en().bit_is_set() && control.core1_global_rst_en().bit_is_clear()
44        }
45    }
46}
47
48pub(crate) fn pre_system_reset() {
49    // Match IDF's esp_restart_noos(): reset and stall only the other core.
50    // The caller must remain alive to request the subsequent system reset.
51    let other_core = match Cpu::current() {
52        Cpu::ProCpu => Cpu::AppCpu,
53        Cpu::AppCpu => Cpu::ProCpu,
54    };
55    match other_core {
56        Cpu::ProCpu => LP_AON_CLK_RST::regs()
57            .hpcore0_reset_ctrl()
58            .modify(|_, w| w.hpcore0_sw_reset().set_bit()),
59        Cpu::AppCpu => LP_AON_CLK_RST::regs()
60            .hpcore1_reset_ctrl()
61            .modify(|_, w| w.hpcore1_sw_reset().set_bit()),
62    };
63    unsafe { internal_park_core(other_core, true) };
64
65    // Prevent ROM from jumping to an entry point from the previous image.
66    crate::rom::ets_set_appcpu_boot_addr(0);
67}
68
69pub(crate) fn disable_core1() {
70    // ESP-IDF single-core mode disables both Core 1 clocks and holds the core
71    // in global reset.
72    HP_SYS_CLKRST::regs().hpcore1_ctrl0().modify(|_, w| {
73        w.core1_cpu_clk_en()
74            .clear_bit()
75            .core1_clic_clk_en()
76            .clear_bit()
77            .core1_global_rst_en()
78            .set_bit()
79    });
80}
81
82#[cfg(feature = "unstable")]
83pub(crate) fn start_core1(entry_point: *const u32) {
84    // Enable both clocks and release Core 1 from global reset.
85    HP_SYS_CLKRST::regs().hpcore1_ctrl0().modify(|_, w| {
86        w.core1_cpu_clk_en()
87            .set_bit()
88            .core1_clic_clk_en()
89            .set_bit()
90            .core1_global_rst_en()
91            .clear_bit()
92    });
93
94    // Core 1's ROM waits for this address before handing control to the app.
95    crate::rom::ets_set_appcpu_boot_addr(entry_point as u32);
96}
97
98/// Core 1 entry point set as the boot address.
99///
100/// ROM jumps here directly, bypassing `_start`, so initialize `gp` and the FPU
101/// before entering regular Rust.
102#[unsafe(naked)]
103#[cfg(feature = "unstable")]
104pub(crate) extern "C" fn start_core1_init<F>() -> !
105where
106    F: FnOnce(),
107{
108    core::arch::naked_asm!(
109        ".option push",
110        ".option norelax",
111        "la gp, __global_pointer$",
112        ".option pop",
113        // Follow IDF's FPU initialization, enable it in Initial state, touch
114        // fcsr (which makes the state Dirty), then clear the dirty bit to
115        // leave mstatus.FS in Clean state (0b10).
116        "li t0, 0x2000",
117        "csrs mstatus, t0",
118        "li t0, 1",
119        "csrw fcsr, t0",
120        "li t0, 0x2000",
121        "csrc mstatus, t0",
122        "la t0, {stack_top}",
123        "lw sp, 0(t0)",
124        "j {init}",
125        stack_top = sym multi_core::APP_CORE_STACK_TOP,
126        init = sym start_core1_init_impl::<F>,
127    )
128}
129
130#[cfg(feature = "unstable")]
131fn start_core1_init_impl<F>() -> !
132where
133    F: FnOnce(),
134{
135    crate::soc::enable_branch_predictor();
136    crate::rom::ets_set_appcpu_boot_addr(0);
137
138    unsafe {
139        #[cfg(all(feature = "rt", stack_guard_monitoring))]
140        {
141            let guard =
142                multi_core::APP_CORE_STACK_GUARD.load(core::sync::atomic::Ordering::Acquire);
143            guard.write_volatile(esp_config::esp_config_int!(
144                u32,
145                "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
146            ));
147            crate::debugger::set_stack_watchpoint(guard as usize);
148        }
149        crate::interrupt::init_vectoring();
150    }
151
152    unsafe { multi_core::CpuControl::start_core1_run::<F>() }
153}