ULP Timestamp and support for structs

fededim
Posts: 15
Joined: Sat Apr 11, 2020 8:11 am

ULP Timestamp and support for structs

Postby fededim » Sat Apr 11, 2020 8:18 am

Hi,

I would like to know 2 things on ULP assembler programming:

1) If it is possible to get a timestamp from the internal ESP RTC (e.g. code would be helpful)

2) If the assembler provided has support for structs, e.g. I can define a struct and I can use the member name as offsets in the assembler code (without having to calculate manually all the offsets).

Thanks in advance for any help.

Federico.

fededim
Posts: 15
Joined: Sat Apr 11, 2020 8:11 am

Re: ULP Timestamp and support for structs

Postby fededim » Mon Apr 13, 2020 1:46 pm

:cry: :cry: I fear that getting a timestamp is not possible due to the fact that ULP can only access to lower 16bits of a memory address (this is a very bad restriction). If we see the code of function rtc_time_get

Code: Select all

uint64_t rtc_time_get(void)
{
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    uint64_t t = READ_PERI_REG(RTC_CNTL_TIME0_REG);
    t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32;
    return t;
}
and we try to reproduce it in ulp assembly we understand that is impossible to set the RTC_CNTL_TIME_UPDATE (bit 31) in the 32bit register RTC_CNTL_TIME_UPDATE_REG because the LD and ST assembly instructions can only read lower 16bits from 32bit aligned address (why Espressif haven't you allowed to read any memory region ? :shock: And why do you update the upper 16bits of an address with program counter on ST instruction ? This is craziness you made the ULP unusable :shock: :shock: ). The same problem also happen when trying to read the upper 16bit of RTC_CNTL_TIME0_REG. So we can't use ULP to get sensor data with a timestamp :cry:

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

Re: ULP Timestamp and support for structs

Postby boarchuz » Tue Apr 14, 2020 3:56 am

You can use the REG_RD and REG_WR instructions for registers, which allow you to specify a bit range to write (up to 8 bits) or read (up to 16 bits). There are also macros to help with this: https://docs.espressif.com/projects/esp ... ers-access
eg. To request ticks reg update:

Code: Select all

WRITE_RTC_FIELD(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE, 1)
To read valid bit:

Code: Select all

READ_RTC_FIELD(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID)
To read ticks:

Code: Select all

//[47:32]
READ_RTC_REG(RTC_CNTL_TIME1_REG, 0, 16)
//[31:16]
READ_RTC_REG(RTC_CNTL_TIME0_REG, 16, 16)
//[15:0]
READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)
Of course you would need 3*32-bit aligned variables to ST the full 48 bit counter, one for each 16-bit read.

It's using macros but you might be interested to see my implementation of a ulp_var_t with I_GET and I_PUT ULP instructions as well as ::get() and ::put() for the SoC, example here:
https://github.com/boarchuz/HULP/blob/r ... e/main.cpp
I also have a ulp_timestamp_t for exactly the purpose you have described here but I don't have any examples of it actually being used in that repo: https://github.com/boarchuz/HULP/blob/7 ... pes.h#L182

fededim
Posts: 15
Joined: Sat Apr 11, 2020 8:11 am

Re: ULP Timestamp and support for structs

Postby fededim » Tue Apr 14, 2020 7:18 am

Boom you lit up the darkness with your reply....I have seen that all the macros you wrote in the end use the instructions REG_RD and REG_WR which I did not see initially and they do allow to read/write upper 16 bits of registers (it would be better that did the same with LD/ST instruction)...Ok apart the waste of memory (double space needed) it's feasible...I'll check also your examples, thank you very much for the answer!

fededim
Posts: 15
Joined: Sat Apr 11, 2020 8:11 am

Re: ULP Timestamp and support for structs

Postby fededim » Tue Apr 14, 2020 8:44 am

Just another question about ULP: it is possible to run in parallel the ULP with ESP32 ? The ULP should run periodically collecting sensor data and then when all RTC memory is full it wakes up the ESP32 to flush it to SPIFFS and goes back to deep sleep. So the ULP must be able to collect data while the ESP32 is copying it to SPIFFS.

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

Re: ULP Timestamp and support for structs

Postby boarchuz » Tue Apr 14, 2020 10:34 am

Yes of course. Once it's loaded and started it will do its own thing regardless of what the SoC is up to.

fededim
Posts: 15
Joined: Sat Apr 11, 2020 8:11 am

Re: ULP Timestamp and support for structs

Postby fededim » Sun Apr 19, 2020 8:49 am

Ok I am experimenting with ULP, sorry I can't use your library HULP because I am using Arduino framework, so I wrote a simple ULP programs which wakes up the ESP32 (it prints some text) and then sleeps for 5 seconds. The wake up works just for the first time but then nothing happens for the next ones (ESP32 stays in deep sleep).

Here it is my ULP code

Code: Select all

  .global entry
entry:   
    is_rdy_for_wakeup_top:
    READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) // Read RTC_CNTL_RDY_FOR_WAKEUP bit
    AND r0, r0, 1
    JUMP is_rdy_for_wakeup_top, eq    // Retry until the bit is set
    WAKE                          // Trigger wake up    

    SLEEP 0
    jump entry			// shouldn't be necessary
Obviously I set up the sleep period in ESP32 C code the first time it boots using ulp_set_wakeup_period(0,5000000).

Do you have an idea of why this is happening ?

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

Re: ULP Timestamp and support for structs

Postby boarchuz » Sun Apr 19, 2020 1:54 pm

Post the rest of your code

fededim
Posts: 15
Joined: Sat Apr 11, 2020 8:11 am

Re: ULP Timestamp and support for structs

Postby fededim » Sun Apr 19, 2020 2:34 pm

Thanks, I managed to solve the problem. You have to call esp_sleep_enable_ulp_wakeup() every time (I believed that you could call it only once) before calling esp_deep_sleep_start().
Last edited by fededim on Sun Apr 19, 2020 7:52 pm, edited 2 times in total.

fededim
Posts: 15
Joined: Sat Apr 11, 2020 8:11 am

Re: ULP Timestamp and support for structs

Postby fededim » Sun Apr 19, 2020 7:42 pm

Another question on my ulp code (the sleep time is 5 seconds). It should read RTC ticks and save them into sdata[0], sdata[1], sdata[2], but unluckily it doesn't since it always prints a strange value which is always the same (there is also an I2C read for BME280, but I skipped it since it does not work, the ESP32 never gets awaken by wake instruction near is_rdy_for_wakeup, now I want to fix the RTC part).

Code: Select all

#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"
#include "soc/rtc_i2c_reg.h"

#include "globaldef.h"

/* Define variables, which go into .bss section (zero-initialized data) */
    .bss

.global samplecount
samplecount: .long 0      // long is 32bit in ulp

// rtc 48 bit diventano 96, 3 int da 32 (ognuno memorizza i 16 bit)
//pressione 20 bit diventano 64, 2 int da 32 (uno i primi sedici, l'altro i rimanenti 4)
//temperatura 20 bit diventano 64, 2 int da 32 (uno i primi sedici, l'altro i rimanenti 4)
//umidità 16 bit 2 byte diventa 32 bit, 1 int da 32.

.global sdata
sdata: .fill MAX_BME280_SAMPLES*(4*3+4*2+4*2+4*1)  // 3 int for RTC 2 int for pressure 2 int for temperature 1 int for humidity total 32 bytes

/* Code goes into .text section */
    .text

    .global entry
entry:   
    // Release hold on pins, so that we can manipulate them
    WRITE_RTC_REG(RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_HOLD_S, RTC_IO_TOUCH_PAD1_HOLD_M, 0);
    WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_HOLD_S, RTC_IO_TOUCH_PAD0_HOLD_M, 0);

    // Select SDA/SCL pins to version 0 TOUCH0 and TOUCH1 (version 1 TOUCH2 and TOUCH3)
    WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SDA_SEL_S, RTC_IO_SAR_I2C_SDA_SEL_M, 0);
    WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SCL_SEL_S, RTC_IO_SAR_I2C_SCL_SEL_M, 0);

    // Set slave address for i2c device we are talking to (ADDR1_REG contains both ADDR0 and ADDR1)
    WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, SENS_I2C_SLAVE_ADDR0_M, I2C_ADDR);
    WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR1_S, SENS_I2C_SLAVE_ADDR1_M, I2C_ADDR);

    // Set SCL speed to 100khz
    WRITE_RTC_REG(RTC_I2C_SCL_LOW_PERIOD_REG, RTC_I2C_SCL_LOW_PERIOD_S, RTC_I2C_SCL_LOW_PERIOD_M, 40);
    WRITE_RTC_REG(RTC_I2C_SCL_HIGH_PERIOD_REG, RTC_I2C_SCL_HIGH_PERIOD_S, RTC_I2C_SCL_HIGH_PERIOD_M, 40);

    // SDA duty (delay) cycles from falling edge of SCL when SDA changes.
    WRITE_RTC_REG(RTC_I2C_SDA_DUTY_REG, RTC_I2C_SDA_DUTY_S, RTC_I2C_SDA_DUTY_M, 16);

    // Number of cycles after start/stop condition
    WRITE_RTC_REG(RTC_I2C_SCL_START_PERIOD_REG, RTC_I2C_SCL_START_PERIOD_S, RTC_I2C_SCL_START_PERIOD_M, 30);
    WRITE_RTC_REG(RTC_I2C_SCL_STOP_PERIOD_REG, RTC_I2C_SCL_STOP_PERIOD_S, RTC_I2C_SCL_STOP_PERIOD_M, 44);

    // cycles before timeout
    WRITE_RTC_REG(RTC_I2C_TIMEOUT_REG, RTC_I2C_TIMEOUT_S, RTC_I2C_TIMEOUT_M, 10000);

    // Set mode to master
    WRITE_RTC_REG(RTC_I2C_CTRL_REG, RTC_I2C_MS_MODE_S, RTC_I2C_MS_MODE_M, 1);

main:
    // Main code
    move R3,sdata

    // READ RTC 48 bit counter
    WRITE_RTC_FIELD(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE, 1)
waitSampleRtc:
    READ_RTC_FIELD(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID)
    jumpr waitSampleRtc, 0, EQ
    READ_RTC_REG(RTC_CNTL_TIME1_REG, 0, 16)      //[47:32]
    st  R0,R3,0  // sdata[0]
    READ_RTC_REG(RTC_CNTL_TIME0_REG, 16, 16)     //[31:16]
    st  R0,R3,4*1  // sdata[1]
    READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)      //[15:0]
    st  R0,R3,4*2  // sdata[2]    

    // samplecount++
    move r2,samplecount
    ld   r0,r2,0            // r0=m[samplecount]
    add  r0,r0,1            // r0=r0+1
    st   r0,r2,0            // m[samplecount]=r0

    // Wake up ESP32
is_rdy_for_wakeup_top:
    READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) // Read RTC_CNTL_RDY_FOR_WAKEUP bit
    AND r0, r0, 1
    JUMP is_rdy_for_wakeup_top, eq    // Retry until the bit is set
    WAKE                          // Trigger wake up    

    halt
    
    jump    entry

    // Read BME280
    i2c_wr 0xF2,1,2,0,0   // oversampling x1 in 0xF2 register for humidity
    i2c_wr 0xF4,0x25,7,0,0   // oversampling x1 in 0xF4 register for pressure and humidity and mode = forced
    i2c_wr 0xF5,0,7,0,0   // disables SPI, no IIR filter, 0.5 standby in config register 0xF5

waitSampleBme:
    i2c_rd 0xF3,7,0,0     // read status register 0xF3
    and    r0,r0,8        // mask bit 3
    jumpr  waitSampleBme,0,GT


    // Read pressure
    i2c_rd  0xf7,7,0,0    // read reg 0xF7 pressure[19:12] 
    lsh     r1,r0,8       // r1=r0<<8
    and     r1,r1,0xFF00  // mask upper 8 bits

    i2c_rd  0xf8, 7, 0, 0 // read reg 0xF8 pressure [11:4] 
    and     r0,r0,0xFF    // mask first 8bit
    or      r1,r0,r1      // r1= r0|r1
    st      r1,r3,4*3     // sdata[3]=pressure [19:12]|pressure[11:4]
    
    i2c_rd  0xf9, 3, 0, 0 // read reg 0xF9 pressure[3:0] 
    st      r0, r3, 4*4    // sdata[4]=pressure [3:0]


    // Read temp
    i2c_rd  0xfa, 7, 0, 0 // read reg 0xFA temp [19:12] 
    lsh     r1,r0,8       // r1=r0<<8
    and     r1,r1,0xFF00  // mask upper 8 bits

    i2c_rd  0xfb, 7, 0, 0 // read reg 0xFB temp [11:4] 
    and     r0,r0,0xFF    // mask first 8bit
    or      r1,r0,r1      // r1= r0|r1
    st      r1,r3,4*5     // sdata[5]=temp [19:12]|temp[11:4]
    
    i2c_rd  0xfc, 3, 0, 0 // read reg 0xFc temp[3:0] 
    st      r0, r3, 4*6    // sdata[6]=temp [3:0]


    // Read humidity
    i2c_rd  0xfd, 7, 0, 0 // read reg 0xFd humidity [15:8] 
    lsh     r1,r0,8       // r1=r0<<8
    and     r1,r1,0xFF00  // mask upper 8 bits

    i2c_rd  0xfe, 7, 0, 0 // read reg 0xFe humidity [7:0] 
    and     r0,r0,0xFF    // mask first 8bit
    or      r1,r0,r1      // r1= r0|r1
    st      r1,r3,4*7     // sdata[7]=humidity [15:8]|temp[7:0]


    // samplecount++
    move r2,samplecount
    ld   r0,r2,0            // r0=m[samplecount]
    add  r0,r0,1            // r0=r0+1
    st   r0,r2,0            // m[samplecount]=r0
    
    // Wake up ESP32
is_rdy_for_wakeup:
    READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) // Read RTC_CNTL_RDY_FOR_WAKEUP bit
    AND r0, r0, 1
    JUMP is_rdy_for_wakeup, eq    // Retry until the bit is set
    WAKE                          // Trigger wake up    

    halt
    
    jump    entry

/* end the program */
.global exit
exit:
halt

The C++ code to print the values is:

Code: Select all

    // ***** HERE YOUR SKETCH ***** 
        printf("Deep sleep wakeup from ULP\n");
  
        uint32_t *sdata=&ulp_sdata;

        uint64_t cpprtc=rtc_time_get();
        uint64_t ulprtc=(((uint64_t) (sdata[0]&0xFFFF))<<32)|((sdata[1]&0xFFFF)<<16)|(sdata[2]&0xFFFF);
  
        printf("SampleCount %d\n",ulp_samplecount&0xFFFF);
        printf("ULP RTC %ld C++ RTC %ld Diff %ld\n", ulprtc,cpprtc,cpprtc-ulprtc);
Here follows a sample output

21:42:19.705 -> Deep sleep wakeup from ULP
21:42:19.705 -> SampleCount 163
21:42:19.705 -> ULP RTC 1073447104 C++ RTC 125759032 Diff 0


Sample count value is increased, so it's ok. Do you have any idea ?

Who is online

Users browsing this forum: No registered users and 22 guests