Introduction to the ESP32 ULP Co-processor and Assembly Environment Setup

[中文]

This document introduces the ESP32 ULP co-processor and provides a walkthrough for setting up the assesmbly environment.

1. Introduction

The ULP co-processor is an ultra-low-power co-processor that remains powered on whether the main CPU is in operating mode or in deep sleep. Hence, the ULP co-processor makes ESP32 SoC well suited for low-power application scenarios.

The ESP32 ULP co-processor has the following key features:

  • Contains up to 8 KB of SRAM for instructions and data

  • Uses RTC_FAST_CLK, which is 8 MHz

  • Has built-in ADC and I2C interface

  • Works both in normal and deep sleep mode

  • Is able to wake up the digital core or send an interrupt to the CPU

  • Can access peripheral devices, internal sensors and RTC registers

For more information, you can refer to ESP32 Technical Reference Manual.

2. Power Consumption of the ULP Co-processor

The ULP co-processor can perform ADC sampling, read/write I2C sensor, drive RTC GPIO, etc, with minimum power consumption. In some cases, it can be a replacement of the main CPU.

The table below lists the power consumption while the ULP co-processor works in different scenarios.

Scenarios

Power consumption

Deep-sleep

6 μA

Delay Nop

1.4 mA

GPIO Toggle

1.4 mA

SAR_ADC Sampling

2.3 mA

Note: Power consumption listed above is for the ULP co-processor powered by VDD3P3_RTC and running at 8 MHz, without going into deep sleep. These figures may be considerably reduced when the ULP is put into deep sleep and woken up periodically to work efficiently for a short period of time.

3. Set up ULP Co-processor Assembly Environment

The ULP co-processor can only be developed in assembly and compiled using the toolchain. We provide instructions for the major three platforms: Linux, Windows and MacOS.

3.1 Linux

  1. Download the toolchain for Linux(x64) and extract it into a directory.

  2. Add the path to the bin directory of the toolchain to the PATH environment variable. For example, if the directory is /opt/esp32ulp-elf-binutils, then add export PATH=/opt/esp32ulp-elf-binutils/bin:$PATH to .bashrc.

  3. Run source .bashrc to enable the environment variable.

The ULP co-processor assembly enviroment is set up now.

3.2 Windows

  1. Install download the Windows all-in-one toolchain & MSYS2 zip file. Please refer to standard setup of toolchain for windows.

  2. Download ULP toolchain.

  3. Unzlip the ULP toolchain esp32ulp-elf-binutils-win32-... into MSYS2’s opt directory. C:\msys32\opt directory is recommended, since it also contains the ESP32 toolchain xtensa-esp32-elf.

  4. Open esp32_toolchain.sh file in C:\msys32\etc\profile.d directory for editing and add the path to the bin directory of the toolchain to the PATH environment variable, as shown below:

    # This file was created by ESP-IDF windows_install_prerequisites.sh
    # and will be overwritten if that script is run again.
    export PATH="$PATH:/opt/xtensa-esp32-elf/bin:/opt/esp32ulp-elf-binutils/bin"
    
  5. Save the file and restart MSYS2 console.

Now you should be able to compile ULP programs, e.g. system/ulp_adc

3.3 MacOS

It’s pretty much the same way as on Linux. Download ULP toolchain for MacOS, extract it, and add it to your PATH. 1. Download the toolchain for MacOS. 2. Extract the toolchain into a directory, and add the path to the bin/ directory of the toolchain to the PATH environment variable.

4. Compiling ULP code

Follow the steps below to compile ULP code as part of a component:

1.ULP code, written in assembly, must be added to one or more files with .S extension . These files must be placed into a separate directory inside component directory, for instance ulp/.

  1. Modify the component makefile, adding the following:

ULP_APP_NAME ?= ulp_$(COMPONENT_NAME)
ULP_S_SOURCES = $(COMPONENT_PATH)/ulp/ulp_source_file.S
ULP_EXP_DEP_OBJECTS := main.o
include $(IDF_PATH)/components/ulp/component_ulp_common.mk

Each line is explained in Compiling ULP Code.

  1. Build the application as usual (e.g. make app).

5. Assembly Instructions

There are 24 assembly instructions for the ULP co-processor that are documented in details in ULP coprocessor instruction set.

Algorithm and logical instructions

  • Algorithm instructions: ADD (add to register), SUB (subtract from register)

  • Logical instructions: AND, OR

  • Logical shift instructions: LSH (logical shift left), RSH (logical shift right)

  • Register instructions: MOVE (move to register)

  • Count register instructions: STAGE_RST (reset stage count register), STAGE_INC (increment stage count register), STAGE_DEC (decrement stage count register)

Data loading and storing instructions

  • Load data from memory: LD

  • Store data to memory: ST

  • Read from peripheral register: REG_RD

  • Write to peripheral register: REG_WR

Jumping instructions

  • Jump to an absolute address: JUMP

  • Jump to a relative offset (condition based on R0): JUMPR

  • Jump to a relative address (condition based on stage count): JUMPS

Measurement instructions

  • Measurement with ADC: ADC

  • Measurement with temperature sensor: TSENS

I2C communication instructions

  • Read single byte from I2C slave: I2C_RD

  • Write single byte to I2C slave: I2C_WR

Program execution management instructions

  • Wait some number of cycles: WAIT

  • End the program: HALT

ULP sleep period setting instructions

  • Set ULP wakeup timer period: SLEEP

CPU waking instructions

  • Wake up the CPU: WAKE

6. Examples

You can find some ULP co-processor examples within esp-iot-solution. More examples will be added later.

No.

Examples

Note

1

ulp_hall_sensor

An example of how to read hall sensor in ULP mode

2

ulp_rtc_gpio

An example of how to operate RTC GPIO pins toggle in ULP mode

3

ulp_tsens

An example of how to read on-chip temperature sensor in ULP mode

4

ulp_watering_device

An application demo of watering machine based on ESP32 ULP co-processor