Another ULP timer question

wevets
Posts: 112
Joined: Sat Mar 09, 2019 2:56 am

Another ULP timer question

Postby wevets » Sat Sep 28, 2019 5:51 am

The ULP WAIT instruction appears to have no effect when the system is in deep sleep. With a 150 kHz timer clock giving a tick period of about 6.6 µS, "WAIT 1500000" should cause a delay of about 10 seconds. It doesn't. It seems to cause no delay at all. It assembles and builds with no error, but has no effect.
Does some register or bit not mentioned in the ULP instruction set "read the docs" write up or the tech reference have to be set or cleared somewhere in order for WAIT to work?
Also, if it did work, would this instruction cause the ULP to spin or should it put the ULP to sleep as if the "sleep n" instruction had been used.

Also, there's a curious note in the ULP instruction set write up for the "sleep" instruction".

"However, please note that SLEEP instruction issued (from ULP program) while the system is in deep sleep mode does not have effect, and sleep cycle count 0 is used."

It would appear that the only delay one can cause when going into deep sleep and starting the ULP is what is programmed into the SENS_ULP_CP_SLEEP_CYC0_REG register, since that is the delay invoked on ULP startup before any assembly code is executed, and it appears to be not possible to execute a "SLEEP n" for any "n" while in deep sleep ULP mode. I don't even see any effect from a 2nd "SLEEP 0" instruction. And the WAIT instruction appears not to work. Other than the first execution of SLEEP 0, no other delay seems possible, other than a jump-spin-count loop, when in deep sleep ULP mode. In case it's relevant, I'm doing these tests on chip revision 1.

Do I have this right, or am I missing something pretty elementary. And if I am, shouldn't something about that be mentioned in the ULP instruction set reference?

Forgive me if these are naive questions, but I haven't programmed in almost 30 years, and am still spinning up on the ESP32. I'm spending so much effort on ULP assembly language as I'm aiming for a very low power, long unattended battery life project. The only assembly languages I've used seriously are INS8080, Z80 and x86, all very well documented. Still I could tell you some stories about hardware bugs......

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Another ULP timer question

Postby WiFive » Sat Sep 28, 2019 8:03 am

The ulp clock is 8mhz (rtc fast clk), didn't we cover this?

https://github.com/espressif/esp-iot-so ... dev.S#L159

8000 ticks = 1ms

The wait operand is 16-bit so the max is ~8ms.

The way to sleep is by calling halt. Check out all the ulp examples in esp-iot-solution.
Last edited by WiFive on Sat Sep 28, 2019 8:21 am, edited 1 time in total.

User avatar
ESP_krzychb
Posts: 394
Joined: Sat Oct 01, 2016 9:05 am
Contact:

Re: Another ULP timer question

Postby ESP_krzychb » Sat Sep 28, 2019 8:17 am

wevets wrote:
Sat Sep 28, 2019 5:51 am
The ULP WAIT instruction appears to have no effect when the system is in deep sleep.

Hi wevets,

When the system is in deep sleep ULP is halted (ULP program is stopped), so the ULP WAIT instruction has no effect.
Please also see diagram on page 614 of the latest ESP32 Technical Reference Manual V4.0.

wevets
Posts: 112
Joined: Sat Mar 09, 2019 2:56 am

Re: Another ULP timer question

Postby wevets » Mon Sep 30, 2019 5:14 am

As suggested, I looked in all the esp-iot-edf ulp examples. I cloned all of esp-iot-edf to my computer for easy searching. I did a “grep -ir –include=*.S wait” above all the .S files in the directory structure. There were several calls to “wait n” in those files, mostly for short durations, durations that might not have been noticed or have any actual effect. So I wrote a minimal C program that:
1. Calls ulp_set_wakeup_period(0, 5000000), enough to delay ~5 seconds at the start of a ULP assembly snippet, and then puts the system into deep sleep.
2. Starts a ULP snippent that does nothing but calls “WAIT 32767” 1220 times, enough to cause about a 5 second delay assuming the 8 MHz ULP clock.

I expected a 10 second delay between print statements from the C program with the wait loop in the ULP snippet. I got 5 seconds. I got 5 seconds when I commented out the “wait” loop in the ULP program, and when I didn’t comment it out. The wait loop appears to have had no effect or and caused no delay whatsoever.

I looked at the programs in the ULP samples to which you pointed me in “esp-iot-solution”, and it was clear that in no case would the fact that “WAIT” did not work would that have been noticed or have any negative effect.

I include my test C and ULP program here and I invite you to do the tests I did. Both as near as I can tell adhere to what’s in both the “ESP32 Technical Reference” and the “Read The Docs”, and they compile and assemble with no errors or warnings. If I’ve missed something, if there’s some bit in some register I should set or clear somewhere, please let me know and, if possible, make things clearer in the documentation. Is this a bug in the SDK, or, worse, a hardware bug (I’ve found them in the past), or am I doing something wrong in my code (which I’ve also done before….. many times. It's almost always the software.)?
  1. // TestBed.c
  2. #include <stdio.h>
  3. #include <esp32/ulp.h>
  4. #include "esp_sleep.h"
  5. #include "ulp_main.h"
  6.  
  7. extern const uint8_t bin_start[] asm("_binary_ulp_main_bin_start");
  8. extern const uint8_t bin_end[]   asm("_binary_ulp_main_bin_end");
  9.  
  10. extern uint32_t ulp_LoopCount;
  11.  
  12. void app_main()
  13. {
  14.     if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_ULP)   // This block runs once
  15.     {
  16.         ulp_set_wakeup_period( 0, 5000000 );    //Set ULP wake up for ~5 seconds
  17.         ulp_load_binary(0, bin_start, (bin_end - bin_start) / sizeof(uint32_t));
  18.     }
  19.     else
  20.     {
  21.         ulp_LoopCount &= 0x0000FFFF;    // zero the high 16 bits of ulp_LoopCount
  22.         printf("Yooo Hooo - LoopCount is: %d\n\n", ulp_LoopCount);
  23.         ulp_LoopCount = 0;          // Set up ulp_LoopCount for a 5 sec. delay
  24.         printf("But now LoopCount is %u again\n\n", ulp_LoopCount);
  25.     }
  26.        
  27.     // Start ULP and go to sleep
  28.     ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
  29.     esp_sleep_enable_ulp_wakeup();
  30.     esp_deep_sleep_start();
  31. }
  1. //  TestBed.S
  2.     .bss
  3.     .global LoopCount
  4. LoopCount:
  5.     .long 0
  6.     .text
  7.  
  8.     .global entry
  9. entry:
  10.     move    r3, LoopCount
  11.     ld      r0, r3, 0
  12.    
  13. // The following block should cause a 5-second delay.  That is, 1220 repititions
  14. // of "WAIT 32767" (32767 used to avoid any possible issue with the high bit)
  15. // It does not.
  16.  
  17. CountLoop:
  18.     wait    32767
  19.     add     r0, r0, 1
  20.     jumpr   CountLoop, 1220,    LT
  21.  
  22. // End of 5-second delay block
  23.  
  24.     st      r0, r3, 0
  25.     .global exitULP
  26. exitULP:
  27.     wake
  28.     halt

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Another ULP timer question

Postby WiFive » Mon Sep 30, 2019 6:34 am

Wait works, see below.
Last edited by WiFive on Mon Sep 30, 2019 9:47 am, edited 1 time in total.

wevets
Posts: 112
Joined: Sat Mar 09, 2019 2:56 am

Re: Another ULP timer question

Postby wevets » Mon Sep 30, 2019 8:16 am

In addition to my problem with the wait instruction, I am still not able to get the sleep instruction to work. Using what you told me above, I cloned all the ULP examples in the Iot-esp-solution area of git. I searched every .S file using
grep -ri –include=*.S “sleep[[:blank:]][0-4] (I love grep)
This caught all the attempts to use the sleep instruction in all my tests, but there appears to be not a single sleep instruction in all of iot-esp-iot or in any of the other example .S files I’ve pulled down from git or elsewhere. There are no examples, let alone working examples, of the sleep instruction that I can find. Not a single one.

So I built a minimal test program (which compiles and assembles without error or warning), taking into account your statement above that “The way to sleep is by calling halt, very similar to the one I posted in my last query, and got the same result: When firing up the ULP and putting the SoC into deep sleep, the only delay available is the initial startup delay based on SENS_ULP_CP_SLEEP_CYC0_REG. I invite you to run the sample code I supplied to verify this result. If I’m doing something wrong, please help me find it.

The other SENS_ULP_CP_SLEEP_CYCn_REGs appear to be useless in deep sleep, and the only way to get any delay at all is to wake the SoC, start up the ULP again relying on the delay programmed into the default SENS_ULP_CP_SLEEP_CYC0_REG, and then going back to deep sleep. I can find nothing in any of the documentation that describes this. The documentation, as I read it, and as I’ve poked around in /components/ulp/ulp.c and other sources leads me to believe my test program should work. Are the wait and sleep instructions inoperative in the ULP when the SoC is in deep sleep? What am I missing?
  1. // TestBed.c
  2. #include <stdio.h>
  3. #include <esp32/ulp.h>
  4. #include "esp_sleep.h"
  5. #include "ulp_main.h"
  6.  
  7. extern const uint8_t bin_start[] asm("_binary_ulp_main_bin_start");
  8. extern const uint8_t bin_end[]   asm("_binary_ulp_main_bin_end");
  9.  
  10. extern uint32_t ulp_LoopCount;
  11.  
  12. void app_main()
  13. {
  14.     if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_ULP)   // This block should run just once
  15.     {
  16.         ulp_set_wakeup_period( 0, 5000000 );    //Set ULP wake up for ~5 seconds
  17.         ulp_set_wakeup_period( 2, 10000000 );   //Set up  ~10-second (10x10^6 µS) delay
  18.         ulp_load_binary(0, bin_start, (bin_end - bin_start) / sizeof(uint32_t));
  19.         ulp_LoopCount = 0;  // Redundant:  ulp_load_binary() initializes all .bss to zero
  20.     }
  21.     else
  22.     {
  23.         ulp_LoopCount &= 0x0000FFFF;    // zero the high 16 bits of ulp_LoopCount
  24.         printf("Yooo Hooo - Did LoopCount=%d sleeps\n\n", ulp_LoopCount);
  25.         ulp_LoopCount = 0;              // Set up ulp_LoopCount for a 5 sec. delay
  26.         printf("LoopCount re-initialized to %u\n\n", ulp_LoopCount);
  27.     }
  28.        
  29.     // Start ULP and go to sleep
  30.     esp_sleep_enable_ulp_wakeup();
  31.     // ulp_run will set the RTC_CNTL_ULP_CP_SLP_TIMER_EN bit.
  32.     ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
  33.     esp_deep_sleep_start();
  34. }  
  1. //  TestBed.S
  2.     .bss
  3.     .global LoopCount
  4. LoopCount:
  5.     .long 0
  6.     .text
  7.  
  8.     .global entry
  9. entry:
  10.  
  11.     move    r3, LoopCount
  12.     ld      r0, r3, 0
  13.     add     r0, r0, 1
  14.     jumpr   exitULP,    2,  ge
  15.     st      r0, r3, 0
  16.     sleep   2           // Set sleep time to contents of SENS_ULP_CP_SLEEP_CYC2_REG
  17.     halt                // ULP expected to wake at end of sleep at entry
  18.  
  19.     .global exitULP
  20. exitULP:
  21.     wake
  22.     halt        // Could disable timer, but not necessary?  Would be masked anyway.

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Another ULP timer question

Postby WiFive » Mon Sep 30, 2019 9:25 am

Yes sleep instruction won't work but you can overwrite the value of SENS_ULP_CP_SLEEP_CYC0_REG.

https://github.com/espressif/esp-idf/bl ... 1106-L1107

Wait definitely works because this program works

https://github.com/joba-1/Blink-ULP/blo ... lp/blink.S

wevets
Posts: 112
Joined: Sat Mar 09, 2019 2:56 am

Re: Another ULP timer question

Postby wevets » Mon Sep 30, 2019 8:53 pm

OK. Sleep doesn't work, at least in deep sleep. That's a disappointing bug as I'd intended to use it.

I looked at the blink example you pointed me to, and it does, indeed, work. I made my code look more like the blink program, simplifying it a bit so I could focus on what was important, and it works as advertised pretty well. The 8 MHz clock on my DevKitC is a little fast so my delay measurments, made with the chronograph on my watch, turn in slightly shorter delays than advertised, but no big deal. I don't have a scope or a logic analyzer, so my testing is pretty primative.
There is what I would call a documentation bug in the "read the docs" version of the ULP instruction set reference in that the 16-bit limit on the length of a wait is not called out. This is clear, however, in the Tech Ref's description of the wait instruction if one looks at the instruction diagram. Just for grins, I tried a wait of 0xFFFF and got just under what I would have predicted with an 8 MHz clock. I got 0 wait with "wait 0x10000". Interesting, but not very useful.

One last question on this. During "wait x" is the ULP running at full power, such as full power is for the ULP? I'm guessing something around 5~10 µA? And what I'm really asking is what is the lowest power state I can put the part into and still be recoverable? Does the initial sleep based on the value in SENS_ULP_CP_SLEEP_CYC0_REG when the ULP is started run at lower power than a wait instruction? Has anyone looked at this? (I don't have a way to measure power either, yet.)
Last edited by wevets on Tue Oct 01, 2019 5:23 am, edited 1 time in total.

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Another ULP timer question

Postby WiFive » Mon Sep 30, 2019 9:23 pm

During "wait x" is the ULP running at full power, such as full power is for the ULP?
Yes

Does the initial sleep based on the value in SENS_ULP_CP_SLEEP_CYC0_REG when the ULP is started run at lower power than a wait instruction?
Yes, during halt sleep. (There does not appear to be any initial sleep since if you set SENS_ULP_CP_SLEEP_CYC0_REG to 5sec and run the blink program the led turns on immediately)

Also try this modification with SENS_ULP_CP_SLEEP_CYC0_REG set to 2sec

Code: Select all

/*
 * JoBa1 ESP32 ULP blink example
 *
 * Simple ULP program that initializes a gpio pin and toggles it high/low
 */

#include "soc/soc_ulp.h"     // for WRITE_RTC_REG
#include "soc/rtc_io_reg.h"  // for RTC_GPIO_*


.global entry                // ulp start address used by main core program

.set gpio_2, 12              // gpio pin 2 is rtc pin 12

.text

entry:
  // might be needed for some pads, but surely not #2
  // WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_TO_GPIO_S, 1, 1)

  // use digital function, not rtc function
  WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_S, 1, 1)

  // gpio_2 shall be output, not input
  WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio_2, 1, 1)

on:
  WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS_S + gpio_2, 1, 1)

  move  r1, 2000           // wait in ms
  move  r2, off           // return address
  jump  delay             // call subroutine

off:
  WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC_S + gpio_2, 1, 1)

  move  r1, 800           // wait in ms
  move  r2, on            // return address
  // jump  delay             // call subroutine
  halt                          // use halt sleep for delay

delay:
  wait  8000              // wait 8000 clock ticks at 8MHz -> 1ms
  sub   r1, r1, 1         // decrement ms count
  jump  r2, eq            // if ms count is zero then return to caller
  jump  delay             // else continue to wait


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

Re: Another ULP timer question

Postby boarchuz » Tue Oct 01, 2019 5:40 am

wevets wrote:
Mon Sep 30, 2019 8:53 pm
During "wait x" is the ULP running at full power, such as full power is for the ULP? I'm guessing something around 5~10 µA?
ULP will draw a lot more than that when it's running. I'm not aware of anything can get close to 1uA/MHz currently.

It's probably somewhere in the 200uA-2mA range.

You might consider using the RTC ticks register for any longer timing (or for non-blocking timing). For example, I currently do so to read a bunch of button inputs with debouncing, long presses, etc and handle I2C readings at varying intervals. It's a lot more complex than looping over a delay though.

Who is online

Users browsing this forum: awegel, StanInexeon and 114 guests