Bare-bones support to run code on ULP-RV/LP core (#631)

* Bare-bones support to run code on ULP-RV/LP core

* Add CHANGELOG.md entry
This commit is contained in:
Björn Quentin 2023-07-03 16:15:34 +02:00 committed by GitHub
parent a86c2ac310
commit 996da27f30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 570 additions and 1 deletions

View File

@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add octal PSRAM support for ESP32-S3 (#610)
- Add MD5 functions from ESP ROM (#618)
- Add embassy async `read` support for `uart` (#620)
- Add bare-bones support to run code on ULP-RISCV / LP core (#631)
### Changed

View File

@ -79,6 +79,7 @@ peripherals = [
"bt",
"wifi",
"ieee802154",
"lp_core",
# ROM capabilities
"rom_crc_le",

View File

@ -54,6 +54,7 @@ peripherals = [
"phy",
"wifi",
"psram",
"ulp_riscv_core",
# ROM capabilities
"rom_crc_le",

View File

@ -66,6 +66,7 @@ peripherals = [
"bt",
"wifi",
"psram",
"ulp_riscv_core",
# ROM capabilities
"rom_crc_le",

View File

@ -62,9 +62,13 @@ pub use self::rtc_cntl::{Rtc, Rwdt};
pub use self::soc::cpu_control;
#[cfg(efuse)]
pub use self::soc::efuse;
#[cfg(lp_core)]
pub use self::soc::lp_core;
pub use self::soc::peripherals;
#[cfg(psram)]
pub use self::soc::psram;
#[cfg(ulp_riscv_core)]
pub use self::soc::ulp_core;
#[cfg(any(spi0, spi1, spi2, spi3))]
pub use self::spi::Spi;
#[cfg(any(timg0, timg1))]

View File

@ -0,0 +1,79 @@
//! Control the LP core
use esp32c6 as pac;
use crate::peripheral::{Peripheral, PeripheralRef};
#[derive(Debug, Clone, Copy)]
pub enum LpCoreWakeupSource {
HpCpu,
}
pub struct LpCore<'d> {
_lp_core: PeripheralRef<'d, crate::soc::peripherals::LP_CORE>,
}
impl<'d> LpCore<'d> {
pub fn new(lp_core: impl Peripheral<P = crate::soc::peripherals::LP_CORE> + 'd) -> Self {
crate::into_ref!(lp_core);
Self { _lp_core: lp_core }
}
pub fn stop(&mut self) {
ulp_lp_core_stop();
}
pub fn run(&mut self, wakeup_src: LpCoreWakeupSource) {
ulp_lp_core_run(wakeup_src);
}
}
fn ulp_lp_core_stop() {
let pmu = unsafe { &*pac::PMU::PTR };
pmu.lp_cpu_pwr1
.modify(|_, w| unsafe { w.lp_cpu_wakeup_en().bits(0) });
pmu.lp_cpu_pwr1
.modify(|_, w| w.lp_cpu_sleep_req().set_bit());
}
fn ulp_lp_core_run(wakeup_src: LpCoreWakeupSource) {
let lp_aon = unsafe { &*pac::LP_AON::PTR };
let pmu = unsafe { &*pac::PMU::PTR };
let lp_peri = unsafe { &*pac::LP_PERI::PTR };
// Enable LP-Core
lp_aon.lpcore.modify(|_, w| w.disable().clear_bit());
// Allow LP core to access LP memory during sleep
lp_aon.lpbus.modify(|_, w| w.fast_mem_mux_sel().clear_bit());
lp_aon
.lpbus
.modify(|_, w| w.fast_mem_mux_sel_update().set_bit());
// Enable stall at sleep request
pmu.lp_cpu_pwr0
.modify(|_, w| w.lp_cpu_slp_stall_en().set_bit());
// Enable reset after wake-up
pmu.lp_cpu_pwr0
.modify(|_, w| w.lp_cpu_slp_reset_en().set_bit());
// Set wake-up sources
let src = match wakeup_src {
LpCoreWakeupSource::HpCpu => 0x01,
};
pmu.lp_cpu_pwr1
.modify(|_, w| w.lp_cpu_wakeup_en().variant(src));
// Enable JTAG debugging
lp_peri
.cpu
.modify(|_, w| w.lpcore_dbgm_unavaliable().clear_bit());
// wake up
match wakeup_src {
LpCoreWakeupSource::HpCpu => {
pmu.hp_lp_cpu_comm.write(|w| w.hp_trigger_lp().set_bit());
}
}
}

View File

@ -1,5 +1,6 @@
pub mod efuse;
pub mod gpio;
pub mod lp_core;
pub mod peripherals;
pub mod radio_clocks;

View File

@ -71,4 +71,5 @@ crate::peripherals! {
UHCI0 => true,
USB_DEVICE => true,
RADIO => false,
LP_CORE => false,
}

View File

@ -4,6 +4,7 @@ pub mod peripherals;
#[cfg(psram)]
pub mod psram;
pub mod radio_clocks;
pub mod ulp_core;
pub(crate) mod constants {
pub const I2S_SCLK: u32 = 160_000_000;

View File

@ -51,4 +51,5 @@ crate::peripherals! {
XTS_AES => true,
RADIO => false,
PSRAM => false,
ULP_RISCV_CORE => false,
}

View File

@ -1,7 +1,7 @@
const PSRAM_VADDR: u32 = 0x3f500000;
pub fn psram_vaddr_start() -> usize {
unsafe { PSRAM_VADDR_START as usize }
PSRAM_VADDR_START as usize
}
cfg_if::cfg_if! {

View File

@ -0,0 +1,109 @@
//! Control the ULP RISCV core
use esp32s2 as pac;
use crate::peripheral::{Peripheral, PeripheralRef};
extern "C" {
fn ets_delay_us(delay: u32);
}
#[derive(Debug, Clone, Copy)]
pub enum UlpCoreWakeupSource {
HpCpu,
}
pub struct UlpCore<'d> {
_lp_core: PeripheralRef<'d, crate::soc::peripherals::ULP_RISCV_CORE>,
}
impl<'d> UlpCore<'d> {
pub fn new(lp_core: impl Peripheral<P = crate::soc::peripherals::ULP_RISCV_CORE> + 'd) -> Self {
crate::into_ref!(lp_core);
Self { _lp_core: lp_core }
}
// currently stopping the ULP doesn't work (while following the proedures
// outlines in the TRM) - so don't offer this funtion for now
//
// pub fn stop(&mut self) {
// ulp_stop();
// }
pub fn run(&mut self, wakeup_src: UlpCoreWakeupSource) {
ulp_run(wakeup_src);
}
}
fn ulp_stop() {
let rtc_cntl = unsafe { &*pac::RTC_CNTL::PTR };
rtc_cntl
.ulp_cp_timer
.modify(|_, w| w.ulp_cp_slp_timer_en().clear_bit());
// suspends the ulp operation
rtc_cntl.cocpu_ctrl.modify(|_, w| w.cocpu_done().set_bit());
// Resets the processor
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_shut_reset_en().set_bit());
}
fn ulp_run(wakeup_src: UlpCoreWakeupSource) {
let rtc_cntl = unsafe { &*pac::RTC_CNTL::PTR };
// Reset COCPU when power on
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_shut_reset_en().set_bit());
// Disable ULP timer
rtc_cntl
.ulp_cp_timer
.modify(|_, w| w.ulp_cp_slp_timer_en().clear_bit());
// wait for at least 1 RTC_SLOW_CLK cycle
unsafe {
ets_delay_us(20);
}
// Select ULP-RISC-V to send the DONE signal
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_done_force().set_bit());
ulp_config_wakeup_source(wakeup_src);
// Select RISC-V as the ULP_TIMER trigger target
rtc_cntl.cocpu_ctrl.modify(|_, w| w.cocpu_sel().clear_bit());
// Clear any spurious wakeup trigger interrupts upon ULP startup
unsafe {
ets_delay_us(20);
}
rtc_cntl.int_clr_rtc.write(|w| {
w.cocpu_int_clr()
.set_bit()
.cocpu_trap_int_clr()
.set_bit()
.ulp_cp_int_clr()
.set_bit()
});
}
fn ulp_config_wakeup_source(wakeup_src: UlpCoreWakeupSource) {
match wakeup_src {
UlpCoreWakeupSource::HpCpu => {
// use timer to wake up
let rtc_cntl = unsafe { &*pac::RTC_CNTL::PTR };
rtc_cntl
.ulp_cp_ctrl
.modify(|_, w| w.ulp_cp_force_start_top().clear_bit());
rtc_cntl
.ulp_cp_timer
.modify(|_, w| w.ulp_cp_slp_timer_en().set_bit());
}
}
}

View File

@ -5,6 +5,7 @@ pub mod peripherals;
#[cfg(psram)]
pub mod psram;
pub mod radio_clocks;
pub mod ulp_core;
pub(crate) mod constants {
pub const I2S_SCLK: u32 = 160_000_000;

View File

@ -62,4 +62,5 @@ crate::peripherals! {
XTS_AES => true,
RADIO => false,
PSRAM => false,
ULP_RISCV_CORE => false,
}

View File

@ -0,0 +1,138 @@
//! Control the ULP RISCV core
use esp32s3 as pac;
use crate::peripheral::{Peripheral, PeripheralRef};
extern "C" {
fn ets_delay_us(delay: u32);
}
#[derive(Debug, Clone, Copy)]
pub enum UlpCoreWakeupSource {
HpCpu,
}
pub struct UlpCore<'d> {
_lp_core: PeripheralRef<'d, crate::soc::peripherals::ULP_RISCV_CORE>,
}
impl<'d> UlpCore<'d> {
pub fn new(lp_core: impl Peripheral<P = crate::soc::peripherals::ULP_RISCV_CORE> + 'd) -> Self {
crate::into_ref!(lp_core);
Self { _lp_core: lp_core }
}
pub fn stop(&mut self) {
ulp_stop();
}
pub fn run(&mut self, wakeup_src: UlpCoreWakeupSource) {
ulp_run(wakeup_src);
}
}
fn ulp_stop() {
let rtc_cntl = unsafe { &*pac::RTC_CNTL::PTR };
rtc_cntl
.ulp_cp_timer
.modify(|_, w| w.ulp_cp_slp_timer_en().clear_bit());
// suspends the ulp operation
rtc_cntl.cocpu_ctrl.modify(|_, w| w.cocpu_done().set_bit());
// Resets the processor
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_shut_reset_en().set_bit());
unsafe {
ets_delay_us(20);
}
// above doesn't seem to halt the ULP core - this will
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_clkgate_en().clear_bit());
}
fn ulp_run(wakeup_src: UlpCoreWakeupSource) {
let rtc_cntl = unsafe { &*pac::RTC_CNTL::PTR };
// Reset COCPU when power on
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_shut_reset_en().set_bit());
// The coprocessor cpu trap signal doesnt have a stable reset value,
// force ULP-RISC-V clock on to stop RTC_COCPU_TRAP_TRIG_EN from waking the CPU
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_clk_fo().set_bit());
// Disable ULP timer
rtc_cntl
.ulp_cp_timer
.modify(|_, w| w.ulp_cp_slp_timer_en().clear_bit());
// wait for at least 1 RTC_SLOW_CLK cycle
unsafe {
ets_delay_us(20);
}
// We do not select RISC-V as the Coprocessor here as this could lead to a hang
// in the main CPU. Instead, we reset RTC_CNTL_COCPU_SEL after we have enabled
// the ULP timer.
//
// IDF-4510
// Select ULP-RISC-V to send the DONE signal
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_done_force().set_bit());
// Set the CLKGATE_EN signal
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_clkgate_en().set_bit());
ulp_config_wakeup_source(wakeup_src);
// Select RISC-V as the ULP_TIMER trigger target
// Selecting the RISC-V as the Coprocessor at the end is a workaround
// for the hang issue recorded in IDF-4510.
rtc_cntl.cocpu_ctrl.modify(|_, w| w.cocpu_sel().clear_bit());
// Clear any spurious wakeup trigger interrupts upon ULP startup
unsafe {
ets_delay_us(20);
}
rtc_cntl.int_clr_rtc.write(|w| {
w.cocpu_int_clr()
.set_bit()
.cocpu_trap_int_clr()
.set_bit()
.ulp_cp_int_clr()
.set_bit()
});
rtc_cntl
.cocpu_ctrl
.modify(|_, w| w.cocpu_clkgate_en().set_bit());
}
fn ulp_config_wakeup_source(wakeup_src: UlpCoreWakeupSource) {
match wakeup_src {
UlpCoreWakeupSource::HpCpu => {
// use timer to wake up
let rtc_cntl = unsafe { &*pac::RTC_CNTL::PTR };
rtc_cntl
.ulp_cp_ctrl
.modify(|_, w| w.ulp_cp_force_start_top().clear_bit());
rtc_cntl
.ulp_cp_timer
.modify(|_, w| w.ulp_cp_slp_timer_en().set_bit());
}
}
}

View File

@ -0,0 +1,83 @@
//! This shows a very basic example of running code on the LP core.
//!
//! Code on LP core just increments a counter. The current value is printed by
//! the HP core.
#![no_std]
#![no_main]
use esp32c6_hal::{
clock::ClockControl,
peripherals::Peripherals,
prelude::*,
timer::TimerGroup,
Rtc,
};
use esp_backtrace as _;
use esp_hal_common::lp_core;
use esp_println::println;
// 50000000 <_start>:
// 50000000: 00000517 auipc a0,0x0
// 50000004: 01050513 addi a0,a0,16 # 50000010 <data>
// 50000008: 4581 li a1,0
//
// 5000000a <_loop>:
// 5000000a: 0585 addi a1,a1,1
// 5000000c: c10c sw a1,0(a0)
// 5000000e: bff5 j 5000000a <_loop>
//
// 50000010 <data>:
// 50000010: 0000 0000
const CODE: &[u8] = &[
0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x01, 0x81, 0x45, 0x85, 0x05, 0x0c, 0xc1, 0xf5, 0xbf,
0x00, 0x00, 0x00, 0x00,
];
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take();
let mut system = peripherals.PCR.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let mut rtc = Rtc::new(peripherals.LP_CLKRST);
let timer_group0 = TimerGroup::new(
peripherals.TIMG0,
&clocks,
&mut system.peripheral_clock_control,
);
let mut wdt0 = timer_group0.wdt;
let timer_group1 = TimerGroup::new(
peripherals.TIMG1,
&clocks,
&mut system.peripheral_clock_control,
);
let mut wdt1 = timer_group1.wdt;
// Disable watchdog timers
rtc.swd.disable();
rtc.rwdt.disable();
wdt0.disable();
wdt1.disable();
let mut lp_core = esp32c6_hal::lp_core::LpCore::new(peripherals.LP_CORE);
lp_core.stop();
println!("lp core stopped");
// copy code to LP ram
let lp_ram = 0x5000_0000 as *mut u8;
unsafe {
core::ptr::copy_nonoverlapping(CODE as *const _ as *const u8, lp_ram, CODE.len());
}
println!("copied code (len {})", CODE.len());
// start LP core
lp_core.run(lp_core::LpCoreWakeupSource::HpCpu);
println!("lpcore run");
let data = (0x5000_0010 - 0) as *mut u32;
loop {
println!("Current {}", unsafe { data.read_volatile() });
}
}

View File

@ -0,0 +1,72 @@
//! This shows a very basic example of running code on the ULP RISCV core.
//!
//! Code on ULP core just increments a counter. The current value is printed by
//! the HP core.
#![no_std]
#![no_main]
use esp32s2_hal::{
clock::ClockControl,
peripherals::Peripherals,
prelude::*,
timer::TimerGroup,
Rtc,
};
use esp_backtrace as _;
use esp_println::println;
// 50000000 <_start>:
// 50000000: 00000517 auipc a0,0x0
// 50000004: 01050513 addi a0,a0,16 # 50000010 <data>
// 50000008: 4581 li a1,0
//
// 5000000a <_loop>:
// 5000000a: 0585 addi a1,a1,1
// 5000000c: c10c sw a1,0(a0)
// 5000000e: bff5 j 5000000a <_loop>
//
// 50000010 <data>:
// 50000010: 0000 0000
const CODE: &[u8] = &[
0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x01, 0x81, 0x45, 0x85, 0x05, 0x0c, 0xc1, 0xf5, 0xbf,
0x00, 0x00, 0x00, 0x00,
];
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take();
let mut system = peripherals.SYSTEM.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let timer_group0 = TimerGroup::new(
peripherals.TIMG0,
&clocks,
&mut system.peripheral_clock_control,
);
let mut wdt = timer_group0.wdt;
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
// Disable MWDT and RWDT (Watchdog) flash boot protection
wdt.disable();
rtc.rwdt.disable();
let mut ulp_core = esp32s2_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE);
// copy code to RTC ram
let lp_ram = 0x5000_0000 as *mut u8;
unsafe {
core::ptr::copy_nonoverlapping(CODE as *const _ as *const u8, lp_ram, CODE.len());
}
println!("copied code (len {})", CODE.len());
// start ULP core
ulp_core.run(esp32s2_hal::ulp_core::UlpCoreWakeupSource::HpCpu);
println!("ulpcore run");
let data = (0x5000_0010 - 0) as *mut u32;
loop {
println!("Current {}", unsafe { data.read_volatile() });
}
}

View File

@ -0,0 +1,74 @@
//! This shows a very basic example of running code on the ULP RISCV core.
//!
//! Code on ULP core just increments a counter. The current value is printed by
//! the HP core.
#![no_std]
#![no_main]
use esp32s3_hal::{
clock::ClockControl,
peripherals::Peripherals,
prelude::*,
timer::TimerGroup,
Rtc,
};
use esp_backtrace as _;
use esp_println::println;
// 50000000 <_start>:
// 50000000: 00000517 auipc a0,0x0
// 50000004: 01050513 addi a0,a0,16 # 50000010 <data>
// 50000008: 4581 li a1,0
//
// 5000000a <_loop>:
// 5000000a: 0585 addi a1,a1,1
// 5000000c: c10c sw a1,0(a0)
// 5000000e: bff5 j 5000000a <_loop>
//
// 50000010 <data>:
// 50000010: 0000 0000
const CODE: &[u8] = &[
0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x01, 0x81, 0x45, 0x85, 0x05, 0x0c, 0xc1, 0xf5, 0xbf,
0x00, 0x00, 0x00, 0x00,
];
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take();
let mut system = peripherals.SYSTEM.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let timer_group0 = TimerGroup::new(
peripherals.TIMG0,
&clocks,
&mut system.peripheral_clock_control,
);
let mut wdt = timer_group0.wdt;
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
// Disable MWDT and RWDT (Watchdog) flash boot protection
wdt.disable();
rtc.rwdt.disable();
let mut ulp_core = esp32s3_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE);
ulp_core.stop();
println!("ulp core stopped");
// copy code to RTC ram
let lp_ram = 0x5000_0000 as *mut u8;
unsafe {
core::ptr::copy_nonoverlapping(CODE as *const _ as *const u8, lp_ram, CODE.len());
}
println!("copied code (len {})", CODE.len());
// start ULP core
ulp_core.run(esp32s3_hal::ulp_core::UlpCoreWakeupSource::HpCpu);
println!("ulpcore run");
let data = (0x5000_0010 - 0) as *mut u32;
loop {
println!("Current {}", unsafe { data.read_volatile() });
}
}