Reading system time in ULP

badaboum
Posts: 4
Joined: Thu Oct 26, 2017 11:18 am
Location: Singapore

Reading system time in ULP

Postby badaboum » Thu Nov 05, 2020 3:08 am

Hello,

I'm trying to access the system time from the ULP. My idea for later is to have the main SoC get the real world time via NTP and store into the RTC slow memory whatever information required to allow the ULP to compute the real world time from the RTC Timer/RTC_SLOW_CLK.

But I'm already having trouble to read the RTC Timer in ULP. I put a minimal example on GitHub: https://github.com/heiko-r/esp32_ulp_timer

In short, this is my main.c:

Code: Select all

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp32/ulp.h"
#include "ulp_main.h"

extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");

void app_main(void)
{
    printf("Hello world!\n");

    /* Load ULP binary */
    esp_err_t err = ulp_load_binary(0, ulp_main_bin_start,
        (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
    ESP_ERROR_CHECK(err);

    /* Set ULP wake up period to T = 20ms. */
    ulp_set_wakeup_period(0, 20000);

    /* Start ULP program */
    err = ulp_run(&ulp_entry - RTC_SLOW_MEM);
    ESP_ERROR_CHECK(err);

    const TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
    while (true) {
        vTaskDelay(xDelay);
        printf("time_reg_1: %10d\n", ulp_time_reg_1 & UINT16_MAX);
        printf("time_reg_2: %10d\n", ulp_time_reg_2 & UINT16_MAX);
        printf("time_reg_3: %10d\n", ulp_time_reg_3 & UINT16_MAX);
    }
}
And this my ULP code:

Code: Select all

#include "soc/rtc_cntl_reg.h"
#include "soc/soc_ulp.h"

    /* Define variables to go into the .bss section (zero-initialised data) */
    .bss

    .global time_reg_1
time_reg_1:
    .long 0

    .global time_reg_2
time_reg_2:
    .long 0

    .global time_reg_3
time_reg_3:
    .long 0


    /* Code goes into the .text section */
    .text
    .global entry
entry:
    READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)
    MOVE R2, time_reg_1
    ST R0, R2, 0

    REG_RD 0x3FF48010, 31, 16
    MOVE R2, time_reg_2
    ST R0, R2, 0

    REG_RD 0x3FF48014, 15, 0
    MOVE R2, time_reg_3
    ST R0, R2, 0

    HALT
I'm trying to read all RTC Timer registers, and I've tried doing so using READ_RTC_REG as well as directly with REG_RD. But the timer doesn't appear to increase:

Code: Select all

Hello world!
time_reg_1:      54298
time_reg_2:          0
time_reg_3:          0
time_reg_1:      54298
time_reg_2:          0
time_reg_3:          0
time_reg_1:      54298
time_reg_2:          0
time_reg_3:          0
The value in time_reg_1 is a little different with each power reset, but it never increases during runtime. What am I missing here?

boarchuz
Posts: 611
Joined: Tue Aug 21, 2018 5:28 am

Re: Reading system time in ULP

Postby boarchuz » Thu Nov 05, 2020 6:16 am

You need to poke the RTC controller to update the register.

eg. with macros it would look something like this:

Code: Select all

// Set update bit
I_WR_REG_BIT(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_S, 1)
// Loop until valid bit is set
I_RD_REG_BIT(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_S)
I_BL(-1, 1)

badaboum
Posts: 4
Joined: Thu Oct 26, 2017 11:18 am
Location: Singapore

Re: Reading system time in ULP

Postby badaboum » Thu Nov 05, 2020 7:01 am

Thanks so much! That works perfectly and I doubt I would have ever figured this out myself!

I decided to use the newer macros instead, and this is what I came up with, in case anyone else runs into this:

Code: Select all

#include "soc/rtc_cntl_reg.h"
#include "soc/soc_ulp.h"

    /* Define variables to go into the .bss section (zero-initialised data) */
    .bss

    .global time_reg_1
time_reg_1:
    .long 0

    .global time_reg_2
time_reg_2:
    .long 0

    .global time_reg_3
time_reg_3:
    .long 0


    /* Code goes into the .text section */
    .text
    .global entry
entry:
    /* Trigger update of register */
    WRITE_RTC_REG(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_S, 1, 1)
    JUMP check_time_valid
    

check_time_valid:
    /* Check if RTC_CNTL_TIME_VALID bit is 1, otherwise repeat */
    READ_RTC_REG(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_S, 1)
    AND R0, R0, 1
    JUMP check_time_valid, EQ
    
    /* Read timer registers */
    READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)
    MOVE R2, time_reg_1
    ST R0, R2, 0

    REG_RD 0x3FF48010, 31, 16
    MOVE R2, time_reg_2
    ST R0, R2, 0

    REG_RD 0x3FF48014, 15, 0
    MOVE R2, time_reg_3
    ST R0, R2, 0

    HALT
  

Michaelboeding
Posts: 8
Joined: Tue Aug 29, 2023 2:12 pm

Re: Reading system time in ULP

Postby Michaelboeding » Thu Oct 24, 2024 4:46 am

How would you do this in C for the RISC-V ULP?

MicroController
Posts: 1750
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Reading system time in ULP

Postby MicroController » Thu Oct 24, 2024 5:00 pm

On the ESP32-S3's RISC-V ULP, you can do it like this:

Code: Select all

#include "soc/rtc_cntl_reg.h"
#include "ulp_riscv_register_ops.h"

/**
 * @brief Read the lower 32 bits of the real time clock counter.
 * @note At 150kHz, this value will overflow every ~7.95h.
 * 
 * @return uint32_t 
 */
static inline uint32_t ulp_riscv_get_rtc_32() {
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    return READ_PERI_REG(RTC_CNTL_TIME0_REG);
}

/**
 * @brief Read the full real time clock counter value (48 bits).
 * 
 * @return uint64_t 
 */
static inline uint64_t ulp_riscv_get_rtc() {
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    return (((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32) | READ_PERI_REG(RTC_CNTL_TIME0_REG);
}

Michaelboeding
Posts: 8
Joined: Tue Aug 29, 2023 2:12 pm

Re: Reading system time in ULP

Postby Michaelboeding » Fri Oct 25, 2024 5:37 am

Thanks so much! Ill try to implement this now and just use the interrupts and the timestamp of the RTC in the ulp!

Michaelboeding
Posts: 8
Joined: Tue Aug 29, 2023 2:12 pm

Re: Reading system time in ULP

Postby Michaelboeding » Fri Oct 25, 2024 10:36 pm

Tried to implement this but it seem to lock up my ulp whenever it tries to call the following.

Code: Select all

// Read RTC timer
SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
rtc_timestamp_low = READ_PERI_REG(RTC_CNTL_TIME0_REG);
rtc_timestamp_high = READ_PERI_REG(RTC_CNTL_TIME1_REG);

Full ULP code

Code: Select all

#include "ulp_riscv.h"
#include "ulp_riscv_utils.h"
#include <stdint.h>
#include "ulp_riscv.h"
#include "ulp_riscv_utils.h"
#include "ulp_riscv_i2c_ulp_core.h"
#include "ulp_riscv.h"
#include "ulp_riscv_utils.h"
#include "ulp_riscv_i2c_ulp_core.h"
#include <stdint.h>
#include "esp_attr.h"
#include "ulp_riscv_utils.h"
#include "ulp_riscv_gpio.h"
#include "soc/rtc_cntl_reg.h"
#include "ulp_riscv_register_ops.h"


// https://github.com/espressif/esp-idf/tree/release/v5.2/examples/system/ulp/ulp_riscv


//timing 
#define INTERRUPT_PERIOD_MS 20  // Software interrupt period in milliseconds
#define SECONDS_TO_WAIT 15       // Desired wait time in seconds
#define INTERRUPT_COUNT ((SECONDS_TO_WAIT * 1000) / INTERRUPT_PERIOD_MS)

#define ULP_RISCV_CYCLES_PER_SEC (ULP_RISCV_CYCLES_PER_MS * 1000)

//pins 
//these are the intterupt pins from the accelerometer
#define WAKEUP_PIN_1 GPIO_NUM_1
#define WAKEUP_PIN_2 GPIO_NUM_2

int sw_int_cnt = 0;
int wake_by_sw = 0;
int wake_by_gpio = 0;
//timestamp from the ulp the current time from the current wakeup 
uint32_t rtc_timestamp_low = 0;
uint32_t rtc_timestamp_high = 0;
//timestamp from the ulp the previous time from the previous wakeup in ulp 
uint32_t rtc_timestamp_low_prev = 0;
uint32_t rtc_timestamp_high_prev = 0;


/**
 * @brief Read the lower 32 bits of the real time clock counter.
 * @note At 150kHz, this value will overflow every ~7.95h.
 * 
 * @return uint32_t 
 */
static inline uint32_t ulp_riscv_get_rtc_32() {
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    return READ_PERI_REG(RTC_CNTL_TIME0_REG);
}

/**
 * @brief Read the full real time clock counter value (48 bits).
 * 
 * @return uint64_t 
 */
static inline uint64_t ulp_riscv_get_rtc() {
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    return (((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32) | READ_PERI_REG(RTC_CNTL_TIME0_REG);
}

// SW Interrupt Handler 
void sw_int_handler(void *arg) {
    sw_int_cnt++;
    //ulp_riscv_wakeup_main_processor();
}

// GPIO Interrupt Handler 
void gpio_int1_handler(void *arg){
    wake_by_gpio = 1;
    //set the previous timestamp 
    rtc_timestamp_low_prev = rtc_timestamp_low;
    rtc_timestamp_high_prev = rtc_timestamp_high;
    // Read RTC timer
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    rtc_timestamp_low = READ_PERI_REG(RTC_CNTL_TIME0_REG);
    rtc_timestamp_high = READ_PERI_REG(RTC_CNTL_TIME1_REG);
    ulp_riscv_wakeup_main_processor();
}

void gpio_int2_handler(void *arg){
    wake_by_gpio = 2;
    //set the previous timestamp 
    rtc_timestamp_low_prev = rtc_timestamp_low;
    rtc_timestamp_high_prev = rtc_timestamp_high;
    // Read RTC timer
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    rtc_timestamp_low = READ_PERI_REG(RTC_CNTL_TIME0_REG);
    rtc_timestamp_high = READ_PERI_REG(RTC_CNTL_TIME1_REG);
    ulp_riscv_wakeup_main_processor();
}

//method used to setup the gpio intterupts 
void setupGpioIntterupts(){
    /* Configure GPIO in input mode for interrupt */
    ulp_riscv_gpio_init(WAKEUP_PIN_1);
    ulp_riscv_gpio_input_enable(WAKEUP_PIN_1);

    ulp_riscv_gpio_init(WAKEUP_PIN_2);
    ulp_riscv_gpio_input_enable(WAKEUP_PIN_2);

    /* Register GPIO interrupt handler */
    ulp_riscv_gpio_isr_register(WAKEUP_PIN_1, ULP_RISCV_GPIO_INTR_POSEDGE, gpio_int1_handler, NULL);
    ulp_riscv_gpio_isr_register(WAKEUP_PIN_2, ULP_RISCV_GPIO_INTR_POSEDGE, gpio_int2_handler, NULL);
}

//method used to setup the software intterupt 
void setupSoftwareIntterupt(){
    //Register SW interrupt handler 
    ulp_riscv_enable_sw_intr(sw_int_handler, NULL);
}

int main(void) {
    wake_by_sw = 0;
    //set the previous timestamp 
    rtc_timestamp_low_prev = 0;
    rtc_timestamp_high_prev = 0;
    rtc_timestamp_low = 0;
    rtc_timestamp_high = 0;
    //setup the gpio intterupts 
    setupGpioIntterupts();
    //setup the software intterupt 
    setupSoftwareIntterupt();

    while (1) {
        /* Trigger SW interrupt */
        ulp_riscv_trigger_sw_intr();

        if ((sw_int_cnt % 5 == 0) && !wake_by_gpio) {
            wake_by_sw = 1;
            ulp_riscv_wakeup_main_processor();
        }
        ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS);
    }
    
    return 0;
}



Who is online

Users browsing this forum: Google [Bot] and 104 guests