ULP Timestamp and support for structs
ULP Timestamp and support for structs
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.
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.
Re: ULP Timestamp and support for structs
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
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 ? 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 ). 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
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;
}
Re: ULP Timestamp and support for structs
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:
To read valid bit:
To read ticks:
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
eg. To request ticks reg update:
Code: Select all
WRITE_RTC_FIELD(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE, 1)
Code: Select all
READ_RTC_FIELD(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID)
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)
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
Re: ULP Timestamp and support for structs
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!
Re: ULP Timestamp and support for structs
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.
Re: ULP Timestamp and support for structs
Yes of course. Once it's loaded and started it will do its own thing regardless of what the SoC is up to.
Re: ULP Timestamp and support for structs
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
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 ?
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
Do you have an idea of why this is happening ?
Re: ULP Timestamp and support for structs
Post the rest of your code
Re: ULP Timestamp and support for structs
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.
Re: ULP Timestamp and support for structs
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).
The C++ code to print the values is:
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 ?
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);
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 13 guests