Skip to main content

esp_hal/soc/esp32/
cpu_control.rs

1//! # Control CPU Cores (ESP32)
2//!
3//! ## Overview
4//!
5//! This module provides essential functionality for controlling
6//! and managing the APP (second) CPU core on the `ESP32` chip. It is used to
7//! start and stop program execution on the APP core.
8
9use core::sync::atomic::Ordering;
10
11use crate::{
12    peripherals::{DPORT, LPWR, SPI0},
13    system::{Cpu, multi_core::*},
14};
15
16pub(crate) unsafe fn internal_park_core(core: Cpu, park: bool) {
17    let c1_value = if park { 0x21 } else { 0 };
18    let c0_value = if park { 0x02 } else { 0 };
19    match core {
20        Cpu::ProCpu => {
21            LPWR::regs()
22                .sw_cpu_stall()
23                .modify(|_, w| unsafe { w.sw_stall_procpu_c1().bits(c1_value) });
24            LPWR::regs()
25                .options0()
26                .modify(|_, w| unsafe { w.sw_stall_procpu_c0().bits(c0_value) });
27        }
28        Cpu::AppCpu => {
29            LPWR::regs()
30                .sw_cpu_stall()
31                .modify(|_, w| unsafe { w.sw_stall_appcpu_c1().bits(c1_value) });
32            LPWR::regs()
33                .options0()
34                .modify(|_, w| unsafe { w.sw_stall_appcpu_c0().bits(c0_value) });
35        }
36    }
37}
38
39/// Returns `true` if the specified core is currently running (not stalled).
40#[instability::unstable]
41pub fn is_running(core: Cpu) -> bool {
42    if core == Cpu::AppCpu {
43        let dport = DPORT::regs();
44        // DPORT_APPCPU_CLKGATE_EN in APPCPU_CTRL_B bit 0 -> needs to be 1 to even be enabled
45        // DPORT_APPCPU_RUNSTALL in APPCPU_CTRL_C bit 0 -> needs to be 0 to not stall
46        if dport
47            .appcpu_ctrl_b()
48            .read()
49            .appcpu_clkgate_en()
50            .bit_is_clear()
51            || dport.appcpu_ctrl_c().read().appcpu_runstall().bit_is_set()
52        {
53            // If the core is not enabled or is stallled, we can take this shortcut
54            return false;
55        }
56    }
57
58    // sw_stall_appcpu_c1[5:0],  sw_stall_appcpu_c0[1:0]} == 0x86 will stall APP CPU
59    // sw_stall_procpu_c1[5:0],  reg_sw_stall_procpu_c0[1:0]} == 0x86 will stall PRO CPU
60    let is_stalled = match core {
61        Cpu::ProCpu => {
62            let c1 = LPWR::regs()
63                .sw_cpu_stall()
64                .read()
65                .sw_stall_procpu_c1()
66                .bits();
67            let c0 = LPWR::regs().options0().read().sw_stall_procpu_c0().bits();
68            (c1 << 2) | c0
69        }
70        Cpu::AppCpu => {
71            let c1 = LPWR::regs()
72                .sw_cpu_stall()
73                .read()
74                .sw_stall_appcpu_c1()
75                .bits();
76            let c0 = LPWR::regs().options0().read().sw_stall_appcpu_c0().bits();
77            (c1 << 2) | c0
78        }
79    };
80
81    is_stalled != 0x86
82}
83
84fn flush_cache(core: Cpu) {
85    let dport_control = DPORT::regs();
86
87    match core {
88        Cpu::ProCpu => {
89            dport_control
90                .pro_cache_ctrl()
91                .modify(|_, w| w.pro_cache_flush_ena().clear_bit());
92            dport_control
93                .pro_cache_ctrl()
94                .modify(|_, w| w.pro_cache_flush_ena().set_bit());
95            while dport_control
96                .pro_cache_ctrl()
97                .read()
98                .pro_cache_flush_done()
99                .bit_is_clear()
100            {}
101
102            dport_control
103                .pro_cache_ctrl()
104                .modify(|_, w| w.pro_cache_flush_ena().clear_bit());
105        }
106        Cpu::AppCpu => {
107            dport_control
108                .app_cache_ctrl()
109                .modify(|_, w| w.app_cache_flush_ena().clear_bit());
110            dport_control
111                .app_cache_ctrl()
112                .modify(|_, w| w.app_cache_flush_ena().set_bit());
113            while dport_control
114                .app_cache_ctrl()
115                .read()
116                .app_cache_flush_done()
117                .bit_is_clear()
118            {}
119            dport_control
120                .app_cache_ctrl()
121                .modify(|_, w| w.app_cache_flush_ena().clear_bit());
122        }
123    };
124}
125
126fn enable_cache(core: Cpu) {
127    let spi0 = SPI0::regs();
128    let dport_control = DPORT::regs();
129
130    match core {
131        Cpu::ProCpu => {
132            spi0.cache_fctrl().modify(|_, w| w.cache_req_en().set_bit());
133            dport_control
134                .pro_cache_ctrl()
135                .modify(|_, w| w.pro_cache_enable().set_bit());
136        }
137        Cpu::AppCpu => {
138            spi0.cache_fctrl().modify(|_, w| w.cache_req_en().set_bit());
139            dport_control
140                .app_cache_ctrl()
141                .modify(|_, w| w.app_cache_enable().set_bit());
142        }
143    };
144}
145
146pub(crate) fn start_core1(entry_point: *const u32) {
147    let dport_control = DPORT::regs();
148
149    flush_cache(Cpu::AppCpu);
150    enable_cache(Cpu::AppCpu);
151
152    dport_control
153        .appcpu_ctrl_d()
154        .write(|w| unsafe { w.appcpu_boot_addr().bits(entry_point as u32) });
155
156    dport_control
157        .appcpu_ctrl_b()
158        .modify(|_, w| w.appcpu_clkgate_en().set_bit());
159    dport_control
160        .appcpu_ctrl_c()
161        .modify(|_, w| w.appcpu_runstall().clear_bit());
162    dport_control
163        .appcpu_ctrl_a()
164        .modify(|_, w| w.appcpu_resetting().set_bit());
165    dport_control
166        .appcpu_ctrl_a()
167        .modify(|_, w| w.appcpu_resetting().clear_bit());
168}
169
170pub(crate) fn start_core1_init<F>() -> !
171where
172    F: FnOnce(),
173{
174    // disables interrupts
175    unsafe {
176        xtensa_lx::interrupt::set_mask(0);
177    }
178
179    // reset cycle compare registers
180    xtensa_lx::timer::set_ccompare0(0);
181    xtensa_lx::timer::set_ccompare1(0);
182    xtensa_lx::timer::set_ccompare2(0);
183
184    unsafe extern "C" {
185        static mut _init_start: u32;
186    }
187
188    // set vector table and stack pointer
189    unsafe {
190        xtensa_lx::set_vecbase(&raw const _init_start);
191        xtensa_lx::set_stack_pointer(APP_CORE_STACK_TOP.load(Ordering::Acquire));
192
193        #[cfg(all(feature = "rt", stack_guard_monitoring))]
194        {
195            let stack_guard = APP_CORE_STACK_GUARD.load(Ordering::Acquire);
196            stack_guard.write_volatile(esp_config::esp_config_int!(
197                u32,
198                "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
199            ));
200            // setting 0 effectively disables the functionality
201            crate::debugger::set_stack_watchpoint(stack_guard as usize);
202        }
203    }
204
205    // Do not call setup_interrupts as that would disable peripheral interrupts, too.
206    unsafe { crate::interrupt::init_vectoring() };
207
208    // Trampoline to run from the new stack.
209    // start_core1_run should _NEVER_ be inlined
210    // as we rely on the function call to use
211    // the new stack.
212    unsafe { CpuControl::start_core1_run::<F>() }
213}