Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement periodic wake up for esp-lp-hal #2145

Open
Szybet opened this issue Sep 11, 2024 · 2 comments
Open

Implement periodic wake up for esp-lp-hal #2145

Szybet opened this issue Sep 11, 2024 · 2 comments
Labels
low power Light-/Deep-Sleep and co-processor support

Comments

@Szybet
Copy link
Contributor

Szybet commented Sep 11, 2024

Motivations

It's an essential feature to use the lp core

This is half a feature request half a very uncooked draft to implement it (If there is a better place to post it, let me know), as I tried following esp-idf code and implemented it in rust. While it's a hacky way for now, first I want it to work, then I can think of ways to implement it for esp-lp-hal. I'm pretty sure any more knowledgeable person will be able to catch the issue. I revised the code twice, checked every "I'm not sure" comment three times, with no solution. I left links to the reference code for easier debugging, I hope it helps

Well here is the code, only for the esp32c6 (for now)

wakeup.rs
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c#L42

use esp32c6_lp::{lp_aon::{store0, store1, STORE0}, lp_timer};

type LP_TIMER = esp32c6_lp::LP_TIMER;
type LP_AON = esp32c6_lp::LP_AON;

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/lp_timer_ll.h#L46
fn lp_timer_ll_counter_snapshot(rtc_cntl: &lp_timer::RegisterBlock) {
    rtc_cntl.update().write(|w| w.main_timer_update().set_bit());
}

// lp_timer_ll_get_counter_value_low(lp_timer_context.dev, 0);
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/lp_timer_ll.h#L36
fn lp_timer_ll_get_counter_value_low(rtc_cntl: &lp_timer::RegisterBlock) -> u32 {
    // Not sure about the counter_lo thing out of nowhere
    rtc_cntl.main_buf0_low().read().bits()
}

// lp_timer_ll_get_counter_value_high(lp_timer_context.dev, 0);
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/lp_timer_ll.h#L41
fn lp_timer_ll_get_counter_value_high(rtc_cntl: &lp_timer::RegisterBlock) -> u32 {
    // Not sure about the counter_hi thing out of nowhere
    rtc_cntl.main_buf0_high().read().bits()
}

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c#L27
fn ulp_lp_core_lp_timer_get_cycle_count() -> u64 {
    let rtc_cntl: &esp32c6_lp::lp_timer::RegisterBlock = unsafe { &*LP_TIMER::ptr() };

    lp_timer_ll_counter_snapshot(rtc_cntl);

    let lo = lp_timer_ll_get_counter_value_low(rtc_cntl);
    let hi = lp_timer_ll_get_counter_value_high(rtc_cntl);

    // https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/lp_timer_types.h#L29
    // What a mess
    // https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/soc/esp32c6/include/soc/soc_caps.h#L441

    // So the first 32 bits are lo
    // The next 16 bits are from the first 16 bits of hi
    // the rest 16 bits is empty
    let bits: u64 = (lo as u64) | ((hi as u64 & 0xFFFF) << 32);
    bits
}

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/clk_tree_ll.h#L803
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/esp_rom/esp32c61/include/esp32c61/rom/rtc.h#L59
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/soc/esp32c6/include/soc/lp_aon_reg.h#L29
// Not sure, LP_AON_STORE1_REG is different from LP_AON_STORE1
fn clk_ll_rtc_slow_load_cal() -> u32 {
    let aon = unsafe { &*LP_AON::PTR };
    aon.store1().read().bits()
}

// const RTC_CLK_CAL_FRACT: u8 = 19
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c#L66C39-L66C56
fn ulp_lp_core_lp_timer_calculate_sleep_ticks(sleep_duration_us: u64) -> u64 {
    // Not sure
    sleep_duration_us * (1 << 19) / clk_ll_rtc_slow_load_cal() as u64
}

fn lp_timer_ll_clear_lp_alarm_intr_status(rtc_cntl: &lp_timer::RegisterBlock) {
    let reg = rtc_cntl.lp_int_clr();
    // https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/lp_timer_ll.h#L61
    // https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/soc/esp32c6/include/soc/lp_timer_struct.h#L74-L81
    let mut readed = unsafe { reg.as_ptr().read() };
    readed |= 1 << 31;
    unsafe { reg.as_ptr().write(readed) };
}

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/lp_timer_ll.h#L25
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c#L23C5-L23C33
// #define TIMER_ID 1
fn lp_timer_ll_set_alarm_target(rtc_cntl: &lp_timer::RegisterBlock, value: u64) {
    let value32 = ((value >> 32) & 0xFFFF) as u32;
    unsafe { rtc_cntl.main_buf1_high().as_ptr().write(value32) };
    // Low
    unsafe { rtc_cntl.main_buf1_low().as_ptr().write((value & 0xFFFFFFFF) as u32) };
}

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/lp_timer_ll.h#L31
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c#L24C5-L24C34
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/soc/esp32c6/include/soc/lp_timer_struct.h#L24
fn lp_timer_ll_set_target_enable(rtc_cntl: &lp_timer::RegisterBlock, value: bool) {
    // Really not sure
    let mut readed = unsafe { rtc_cntl.main_buf1_high().read().bits() };
    if value {
        readed |= 1 << 0; // Set the enable bit to 1
    } else {
        readed &= !(1 << 0); // Set the enable bit to 0
    }
    unsafe { rtc_cntl.main_buf1_high().as_ptr().write(readed) };
}

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c#L20
fn lp_timer_hal_set_alarm_target(value: u64) {
    let rtc_cntl: &esp32c6_lp::lp_timer::RegisterBlock = unsafe { &*LP_TIMER::ptr() };
    lp_timer_ll_clear_lp_alarm_intr_status(rtc_cntl);
    lp_timer_ll_set_alarm_target(rtc_cntl, value);
    lp_timer_ll_set_target_enable(rtc_cntl, true);
}

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c#L42
pub fn ulp_lp_core_lp_timer_set_wakeup_time(sleep_duration_us: u64) {
    let cycle_cnt = ulp_lp_core_lp_timer_get_cycle_count();
    let alarm_target = cycle_cnt + ulp_lp_core_lp_timer_calculate_sleep_ticks(sleep_duration_us);

    lp_timer_hal_set_alarm_target(alarm_target);
}
sleep.rs
type PMU = esp32c6_lp::PMU;

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/hal/esp32c6/include/hal/lp_core_ll.h#L124
// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/soc/esp32c6/include/soc/pmu_struct.h#L460
fn lp_core_ll_request_sleep() {
    // Did not checked
    let mut reg = unsafe { &*PMU::ptr() };
    let mut readed = unsafe { reg.slp_wakeup_cntl0().as_ptr().read() };
    readed |= 1 << 0; // val |= 1 << bit_index;
    unsafe { reg.slp_wakeup_cntl0().as_ptr().write(readed) };
}

// https://github.com/espressif/esp-idf/blob/59e18382702b6986be3d3f55e9ac7763c1397cf7/components/ulp/lp_core/lp_core/lp_core_utils.c#L127
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32c6/api-reference/system/ulp-lp-core.html#_CPPv416ulp_lp_core_haltv
pub fn ulp_lp_core_halt()
{
    lp_core_ll_request_sleep();

    loop {
        // Not sure
        riscv::asm::wfi();
    }
}
@Szybet Szybet added the status:needs-attention This should be prioritized label Sep 11, 2024
@jessebraham jessebraham added low power Light-/Deep-Sleep and co-processor support and removed status:needs-attention This should be prioritized labels Sep 11, 2024
@Szybet
Copy link
Contributor Author

Szybet commented Sep 11, 2024

By sacrificing my soul to some C demons I was able to link libulp.a from esp-idf to my rust esp-lp-hal program and it still does not wake up, I'm starting to thinking I'm not to blame here... What is this magic?

#[cfg(feature = "esp32c6")]
global_asm!(
r#"
.section .init.vector, "ax"
/* This is the vector table. It is currently empty, but will be populated
* with exception and interrupt handlers when this is supported
*/
.align 0x4, 0xff
.global _vector_table
.type _vector_table, @function
_vector_table:
.option push
.option norvc
.rept 32
nop
.endr
.option pop
.size _vector_table, .-_vector_table
.section .init, "ax"
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
__start:
/* setup the stack pointer */
la sp, __stack_top
call rust_main
loop:
j loop
"#
);

@Szybet
Copy link
Contributor Author

Szybet commented Sep 12, 2024

I tried removing the vector thing from the assembler code, it did not work either... I'm out of options :(

I also tried modifying the ulp_lp_core_run function to enable more wakeup sources there, based on https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf page 122 - Still nothing.

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 - IDK
    //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 mut src = match wakeup_src {
        LpCoreWakeupSource::HpCpu => 0x01,
    };

    src |= 0x10; // LP_TIMER

    pmu.lp_cpu_pwr1()
        .modify(|_, w| unsafe { w.lp_cpu_wakeup_en().bits(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());
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
low power Light-/Deep-Sleep and co-processor support
Projects
Status: Todo
Development

No branches or pull requests

2 participants